aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--#main.go#1971
-rw-r--r--README.md119
2 files changed, 111 insertions, 1979 deletions
diff --git a/#main.go# b/#main.go#
deleted file mode 100644
index 7f97ee3..0000000
--- a/#main.go#
+++ /dev/null
@@ -1,1971 +0,0 @@
-package main
-
-import "fmt"
-import "strings"
-import "strconv"
-import "net/http"
-import "database/sql"
-import _ "github.com/lib/pq"
-import "math/rand"
-import "html/template"
-import "time"
-import "regexp"
-import "os/exec"
-import "bytes"
-import "encoding/json"
-import "io/ioutil"
-import "mime/multipart"
-import "os"
-import "bufio"
-import "io"
-import "github.com/gofrs/uuid"
-
-var Port = ":" + GetConfigValue("instanceport")
-var TP = GetConfigValue("instancetp")
-var Domain = TP + "" + GetConfigValue("instance")
-
-var authReq = []string{"captcha","email","passphrase"}
-
-var supportedFiles = []string{"image/gif","image/jpeg","image/png","image/svg+xml","image/webp","image/avif","image/apng","video/mp4","video/ogg","video/webm","audio/mpeg","audio/ogg","audio/wav", "audio/wave", "audio/x-wav"}
-
-var SiteEmail = GetConfigValue("emailaddress") //contact@fchan.xyz
-var SiteEmailPassword = GetConfigValue("emailpass")
-var SiteEmailServer = GetConfigValue("emailserver") //mail.fchan.xyz
-var SiteEmailPort = GetConfigValue("emailport") //587
-
-var ldjson = "application/ld+json"
-var activitystreams = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
-
-func main() {
-
- if _, err := os.Stat("./public"); os.IsNotExist(err) {
- os.Mkdir("./public", 0755)
- }
-
- InitCache()
-
- db := ConnectDB();
-
- db.
-
- defer db.Close()
-
- go MakeCaptchas(db, 100)
-
- *Key = CreateClientKey()
-
- FollowingBoards = GetActorFollowingDB(db, Domain)
-
- Boards = GetBoardCollection(db)
-
- // root actor is used to follow remote feeds that are not local
- //name, prefname, summary, auth requirements, restricted
- if GetConfigValue("instancename") != "" {
- CreateNewBoardDB(db, *CreateNewActor("", GetConfigValue("instancename"), GetConfigValue("instancesummary"), authReq, false))
- }
-
- // Allow access to public media folder
- fileServer := http.FileServer(http.Dir("./public"))
- http.Handle("/public/", http.StripPrefix("/public", neuter(fileServer)))
-
- javascriptFiles := http.FileServer(http.Dir("./static"))
- http.Handle("/static/", http.StripPrefix("/static", neuter(javascriptFiles)))
-
- // main routing
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
- path := r.URL.Path
-
- // remove trailing slash
- if path != "/" {
- re := regexp.MustCompile(`/$`)
- path = re.ReplaceAllString(path, "")
- }
-
- var mainActor bool
- var mainInbox bool
- var mainOutbox bool
- var mainFollowing bool
- var mainFollowers bool
-
- var actorMain bool
- var actorInbox bool
- var actorCatalog bool
- var actorOutbox bool
- var actorPost bool
- var actorFollowing bool
- var actorFollowers bool
- var actorReported bool
- var actorVerification bool
- var actorMainPage bool
-
- var accept = r.Header.Get("Accept")
-
- var method = r.Method
-
- var actor = GetActorFromPath(db, path, "/")
-
- if actor.Name == "main" {
- mainActor = (path == "/")
- mainInbox = (path == "/inbox")
- mainOutbox = (path == "/outbox")
- mainFollowing = (path == "/following")
- mainFollowers = (path == "/followers")
- } else {
- actorMain = (path == "/" + actor.Name)
- actorInbox = (path == "/" + actor.Name + "/inbox")
- actorCatalog = (path == "/" + actor.Name + "/catalog")
- actorOutbox = (path == "/" + actor.Name + "/outbox")
- actorFollowing = (path == "/" + actor.Name + "/following")
- actorFollowers = (path == "/" + actor.Name + "/followers")
- actorReported = (path == "/" + actor.Name + "/reported")
- actorVerification = (path == "/" + actor.Name + "/verification")
-
- escapedActorName := strings.Replace(actor.Name, "*", "\\*", -1)
- escapedActorName = strings.Replace(escapedActorName, "^", "\\^", -1)
- escapedActorName = strings.Replace(escapedActorName, "$", "\\$", -1)
- escapedActorName = strings.Replace(escapedActorName, "?", "\\?", -1)
- escapedActorName = strings.Replace(escapedActorName, "+", "\\+", -1)
- escapedActorName = strings.Replace(escapedActorName, ".", "\\.", -1)
-
- re := regexp.MustCompile("/" + escapedActorName + "/[0-9]{1,2}$")
-
- actorMainPage = re.MatchString(path)
-
- re = regexp.MustCompile("/" + escapedActorName + "/\\w+")
-
- actorPost = re.MatchString(path)
- }
-
- if mainActor {
- if accept == activitystreams || accept == ldjson {
- GetActorInfo(w, db, Domain)
- return
- }
-
- IndexGet(w, r, db)
-
- return
- }
-
- if mainInbox {
- if method == "POST" {
-
- } else {
- w.WriteHeader(http.StatusForbidden)
- w.Write([]byte("404 no path"))
- }
- return
- }
-
- if mainOutbox {
- if method == "GET" {
- GetActorOutbox(w, r, db)
- } else if method == "POST" {
- ParseOutboxRequest(w, r, db)
- } else {
- w.WriteHeader(http.StatusForbidden)
- w.Write([]byte("404 no path"))
- }
- return
- }
-
- if mainFollowing {
- GetActorFollowing(w, db, Domain)
- return
- }
-
- if mainFollowers {
- GetActorFollowers(w, db, Domain)
- return
- }
-
- if actorMain || actorMainPage {
- if accept == activitystreams || accept == ldjson {
- GetActorInfo(w, db, actor.Id)
- return
- }
-
- postNum := strings.Replace(r.URL.EscapedPath(), "/" + actor.Name + "/", "", 1)
-
- page, _ := strconv.Atoi(postNum)
- collection, valid := WantToServePage(db, actor.Name, page)
- if valid {
- OutboxGet(w, r, db, collection)
- }
-
- return
- }
-
- if actorFollowing {
- GetActorFollowing(w, db, actor.Id)
- return
- }
-
- if actorFollowers {
- GetActorFollowers(w, db, actor.Id)
- return
- }
-
- if actorInbox {
- if method == "POST" {
- ParseInboxRequest(w, r, db)
- } else {
- w.WriteHeader(http.StatusForbidden)
- w.Write([]byte("404 no path"))
- }
- return
- }
-
- if actorCatalog {
- collection, valid := WantToServeCatalog(db, actor.Name)
- if valid {
- CatalogGet(w, r, db, collection)
- }
- return
- }
-
- if actorOutbox {
- if method == "GET" {
- GetActorOutbox(w, r, db)
- } else if method == "POST" {
- ParseOutboxRequest(w, r, db)
- } else {
- w.WriteHeader(http.StatusForbidden)
- w.Write([]byte("404 no path"))
- }
- return
- }
-
- if actorReported {
- GetActorReported(w, r, db, actor.Id)
- return
- }
-
- if actorVerification {
- r.ParseForm()
-
- code := r.FormValue("code")
-
- var verify Verify
-
- verify.Board = actor.Id
- verify.Identifier = "post"
-
- verify = GetVerificationCode(db, verify)
-
- auth := CreateTripCode(verify.Code)
- auth = CreateTripCode(auth)
-
- if CreateTripCode(auth) == code {
- w.WriteHeader(http.StatusOK)
- } else {
- w.WriteHeader(http.StatusUnauthorized)
- }
-
- w.Write([]byte(""))
- }
-
- //catch all
- if actorPost {
- if accept == activitystreams || accept == ldjson {
- GetActorPost(w, db, path)
- return
- }
-
- PostGet(w, r, db)
- return
- }
-
- w.WriteHeader(http.StatusForbidden)
- w.Write([]byte("404 no path"))
- })
-
- http.HandleFunc("/post", func(w http.ResponseWriter, r *http.Request){
-
- r.ParseMultipartForm(10 << 20)
-
- file, header, _ := r.FormFile("file")
-
- if(file != nil && header.Size > (7 << 20)){
- w.Write([]byte("7MB max file size"))
- return
- }
-
- if(r.FormValue("inReplyTo") == "" || file == nil) {
- if(r.FormValue("comment") == "" && r.FormValue("subject") == ""){
- w.Write([]byte("Comment or Subject required"))
- return
- }
- }
-
- if(len(r.FormValue("comment")) > 2000) {
- w.Write([]byte("Comment limit 2000 characters"))
- return
- }
-
- if(len(r.FormValue("subject")) > 100 || len(r.FormValue("name")) > 100 || len(r.FormValue("options")) > 100) {
- w.Write([]byte("Name, Subject or Options limit 100 characters"))
- return
- }
-
- if(r.FormValue("captcha") == "") {
- w.Write([]byte("Incorrect Captcha"))
- return
- }
-
- b := bytes.Buffer{}
- we := multipart.NewWriter(&b)
-
- if(file != nil){
- var fw io.Writer
-
- fw, err := we.CreateFormFile("file", header.Filename)
-
- CheckError(err, "error with form file create")
-
- _, err = io.Copy(fw, file)
-
- CheckError(err, "error with form file copy")
- }
-
- reply := ParseCommentForReply(r.FormValue("comment"))
-
- for key, r0 := range r.Form {
- if(key == "captcha") {
- err := we.WriteField(key, r.FormValue("captchaCode") + ":" + r.FormValue("captcha"))
- CheckError(err, "error with writing field")
- }else{
- err := we.WriteField(key, r0[0])
- CheckError(err, "error with writing field")
- }
- }
-
- if(r.FormValue("inReplyTo") == "" && reply != ""){
- err := we.WriteField("inReplyTo", reply)
- CheckError(err, "error with writing inReplyTo field")
- }
-
- we.Close()
-
- req, err := http.NewRequest("POST", r.FormValue("sendTo"), &b)
-
- CheckError(err, "error with post form req")
-
- req.Header.Set("Content-Type", we.FormDataContentType())
- req.Header.Set("Authorization", "Basic " + *Key)
-
- resp, err := http.DefaultClient.Do(req)
-
- CheckError(err, "error with post form resp")
-
- defer resp.Body.Close()
-
- if(resp.StatusCode == 200){
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- var obj ObjectBase
-
- obj = ParseOptions(r, obj)
- for _, e := range obj.Option {
- if(e == "noko" || e == "nokosage"){
- http.Redirect(w, r, Domain + "/" + r.FormValue("boardName") + "/" + shortURL(r.FormValue("sendTo"), string(body)) , http.StatusMovedPermanently)
- return
- }
- }
-
- http.Redirect(w, r, Domain + "/" + r.FormValue("boardName"), http.StatusMovedPermanently)
- return
- }
-
- if(resp.StatusCode == 403){
- w.Write([]byte("Incorrect Captcha"))
- return
- }
-
- http.Redirect(w, r, Domain + "/" + r.FormValue("boardName"), http.StatusMovedPermanently)
- })
-
- http.HandleFunc("/" + *Key + "/", func(w http.ResponseWriter, r *http.Request) {
-
- id, _ := GetPasswordFromSession(r)
-
- actor := GetActorFromPath(db, r.URL.Path, "/" + *Key + "/")
-
- if actor.Id == "" {
- actor = GetActorFromDB(db, Domain)
- }
-
- if id == "" || (id != actor.Id && id != Domain) {
- t := template.Must(template.ParseFiles("./static/verify.html"))
- t.Execute(w, "")
- return
- }
-
- re := regexp.MustCompile("/" + *Key + "/" + actor.Name + "/follow")
- follow := re.MatchString(r.URL.Path)
-
- re = regexp.MustCompile("/" + *Key + "/" + actor.Name)
- manage := re.MatchString(r.URL.Path)
-
- re = regexp.MustCompile("/" + *Key )
- admin := re.MatchString(r.URL.Path)
-
- re = regexp.MustCompile("/" + *Key + "/follow" )
- adminFollow := re.MatchString(r.URL.Path)
-
- if follow || adminFollow {
- r.ParseForm()
-
-
- var followActivity Activity
-
- followActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- followActivity.Type = "Follow"
-
- var nactor Actor
- var obj ObjectBase
- followActivity.Actor = &nactor
- followActivity.Object = &obj
- followActivity.Actor.Id = r.FormValue("actor")
-
- var mactor Actor
- followActivity.Object.Actor = &mactor
- followActivity.Object.Actor.Id = r.FormValue("follow")
- followActivity.To = append(followActivity.To, r.FormValue("follow"))
-
- if followActivity.Actor.Id == Domain && !IsActorLocal(db, followActivity.Object.Actor.Id) {
- w.Write([]byte("main board can only follow local boards. Create a new board and then follow outside boards from it."))
- return
- }
-
- enc, _ := json.Marshal(followActivity)
-
- req, err := http.NewRequest("POST", actor.Outbox, bytes.NewBuffer(enc))
-
- CheckError(err, "error with follow req")
-
- _, pass := GetPasswordFromSession(r)
-
- pass = CreateTripCode(pass)
- pass = CreateTripCode(pass)
-
- req.Header.Set("Authorization", "Basic " + pass)
-
- req.Header.Set("Content-Type", activitystreams)
-
- _, err = http.DefaultClient.Do(req)
-
- CheckError(err, "error with add board follow resp")
-
- FollowingBoards = GetActorFollowingDB(db, Domain)
-
- Boards = GetBoardCollection(db)
-
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
-
- } else if manage && actor.Name != "" {
- t := template.Must(template.ParseFiles("./static/main.html", "./static/manage.html"))
-
- follow := GetActorCollection(actor.Following)
- follower := GetActorCollection(actor.Followers)
- reported := GetActorCollectionReq(r, actor.Id + "/reported")
-
- var following []string
- var followers []string
- var reports []Report
-
- for _, e := range follow.Items {
- following = append(following, e.Id)
- }
-
- for _, e := range follower.Items {
- followers = append(followers, e.Id)
- }
-
- for _, e := range reported.Items {
- var r Report
- r.Count = int(e.Size)
- r.ID = e.Id
- reports = append(reports, r)
- }
-
- localReports := GetLocalReportDB(db, actor.Name)
-
- for _, e := range localReports {
- var r Report
- r.Count = e.Count
- r.ID = e.ID
- reports = append(reports, r)
- }
-
- var adminData AdminPage
- adminData.Following = following
- adminData.Followers = followers
- adminData.Reported = reports
- adminData.Domain = Domain
- adminData.IsLocal = IsActorLocal(db, actor.Id)
-
- adminData.Title = "Manage /" + actor.Name + "/"
- adminData.Boards = Boards
- adminData.Board.Name = actor.Name
- adminData.Board.Actor = actor
- adminData.Key = *Key
- adminData.Board.TP = TP
-
- adminData.Board.Post.Actor = &actor
-
- t.ExecuteTemplate(w, "layout", adminData)
-
- } else if admin || actor.Id == Domain {
-
- t := template.Must(template.ParseFiles("./static/main.html", "./static/nadmin.html"))
-
- actor := GetActor(Domain)
- follow := GetActorCollection(actor.Following).Items
- follower := GetActorCollection(actor.Followers).Items
-
- var following []string
- var followers []string
-
- for _, e := range follow {
- following = append(following, e.Id)
- }
-
- for _, e := range follower {
- followers = append(followers, e.Id)
- }
-
- var adminData AdminPage
- adminData.Following = following
- adminData.Followers = followers
- adminData.Actor = actor.Id
- adminData.Key = *Key
- adminData.Domain = Domain
- adminData.Board.ModCred,_ = GetPasswordFromSession(r)
-
- adminData.Boards = Boards
-
- adminData.Board.Post.Actor = &actor
-
- t.ExecuteTemplate(w, "layout", adminData)
- }
- })
-
- http.HandleFunc("/" + *Key + "/addboard", func(w http.ResponseWriter, r *http.Request) {
-
- var newActorActivity Activity
- var board Actor
- r.ParseForm()
-
- actor := GetActorFromDB(db, Domain)
-
- var restrict bool
- if r.FormValue("restricted") == "True" {
- restrict = true
- } else {
- restrict = false
- }
-
- board.Name = r.FormValue("name")
- board.PreferredUsername = r.FormValue("prefname")
- board.Summary = r.FormValue("summary")
- board.Restricted = restrict
-
- newActorActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- newActorActivity.Type = "New"
- var nactor Actor
- var nobj ObjectBase
- newActorActivity.Actor = &nactor
- newActorActivity.Object = &nobj
- newActorActivity.Actor.Id = actor.Id
- newActorActivity.Object.Actor = &board
-
-
- enc, _ := json.Marshal(newActorActivity)
-
- req, err := http.NewRequest("POST", actor.Outbox, bytes.NewBuffer(enc))
-
- CheckError(err, "error with add board follow req")
-
- _, pass := GetPasswordFromSession(r)
-
- pass = CreateTripCode(pass)
- pass = CreateTripCode(pass)
-
- req.Header.Set("Authorization", "Basic " + pass)
- req.Header.Set("Content-Type", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- CheckError(err, "error with add board follow resp")
-
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- var respActor Actor
-
- err = json.Unmarshal(body, &respActor)
-
- CheckError(err, "error getting actor from body in new board")
-
- //update board list with new instances following
- if resp.StatusCode == 200 {
- var board []ObjectBase
- var item ObjectBase
- var removed bool = false
-
- item.Id = respActor.Id
- for _, e := range FollowingBoards {
- if e.Id != item.Id {
- board = append(board, e)
- } else {
- removed = true
- }
- }
-
- if !removed {
- board = append(board, item)
- }
-
- FollowingBoards = board
-
- Boards = GetBoardCollection(db)
- }
-
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- })
-
- http.HandleFunc("/verify", func(w http.ResponseWriter, r *http.Request){
- if(r.Method == "POST") {
- r.ParseForm()
- identifier := r.FormValue("id")
- code := r.FormValue("code")
-
- var verify Verify
- verify.Identifier = identifier
- verify.Code = code
-
- j, _ := json.Marshal(&verify)
-
- req, err := http.NewRequest("POST", Domain + "/auth", bytes.NewBuffer(j))
-
- CheckError(err, "error making verify req")
-
- req.Header.Set("Content-Type", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- CheckError(err, "error getting verify resp")
-
- defer resp.Body.Close()
-
- rBody, _ := ioutil.ReadAll(resp.Body)
-
- body := string(rBody)
-
- if(resp.StatusCode != 200) {
- t := template.Must(template.ParseFiles("./static/verify.html"))
- t.Execute(w, "wrong password " + verify.Code)
- } else {
-
- sessionToken, _ := uuid.NewV4()
-
- _, err := cache.Do("SETEX", sessionToken, "86400", body + "|" + verify.Code)
- if err != nil {
- t := template.Must(template.ParseFiles("./static/verify.html"))
- t.Execute(w, "")
- return
- }
-
- http.SetCookie(w, &http.Cookie{
- Name: "session_token",
- Value: sessionToken.String(),
- Expires: time.Now().Add(60 * 60 * 48 * time.Second),
- })
-
- http.Redirect(w, r, "/", http.StatusSeeOther)
- }
- } else {
- t := template.Must(template.ParseFiles("./static/verify.html"))
- t.Execute(w, "")
- }
- })
-
- 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, "/")
- _, auth := GetPasswordFromSession(r)
-
- if id == "" || auth == "" {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- return
- }
-
- if !HasAuth(db, auth, actor.Id) {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- return
- }
-
- if !IsIDLocal(db, id) {
- CreateLocalDeleteDB(db, id, "post")
- CloseLocalReportDB(db, id, board)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- }
-
-
- var obj ObjectBase
- obj.Id = id
- obj.Actor = &actor
-
- isOP := CheckIfObjectOP(db, obj.Id)
-
- if !isOP {
- DeleteObjectRequest(db, id)
- DeleteObject(db, obj.Id)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- } else {
- DeleteObjectAndRepliesRequest(db, id)
- DeleteObjectAndReplies(db, obj.Id)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- }
-
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- })
-
- http.HandleFunc("/deleteattach", func(w http.ResponseWriter, r *http.Request){
-
- id := r.URL.Query().Get("id")
-
- _, auth := GetPasswordFromSession(r)
-
- if id == "" || auth == "" {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- 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)
- return
- }
-
- DeleteAttachmentFromFile(db, id)
- DeletePreviewFromFile(db, id)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- })
-
- http.HandleFunc("/report", func(w http.ResponseWriter, r *http.Request){
-
- r.ParseForm()
-
- id := r.FormValue("id")
- board := r.FormValue("board")
- reason := r.FormValue("comment")
- close := r.FormValue("close")
-
- actor := GetActorFromPath(db, id, "/")
- _, auth := GetPasswordFromSession(r)
-
- var captcha = r.FormValue("captchaCode") + ":" + r.FormValue("captcha")
-
- if len(reason) > 100 {
- w.Write([]byte("Report comment limit 100 characters"))
- return
- }
-
- if(!CheckCaptcha(db, captcha)) {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte("captcha required"))
- return
- }
-
- if close == "1" {
- if !HasAuth(db, auth, actor.Id) {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- return
- }
-
- if !IsIDLocal(db, id) {
- CloseLocalReportDB(db, id, board)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- }
-
- reported := DeleteReportActivity(db, id)
- if reported {
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- }
-
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- return
- }
-
- if !IsIDLocal(db, id) {
- CreateLocalReportDB(db, id, board, reason)
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- }
-
- reported := ReportActivity(db, id, reason)
- if reported {
- http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
- return
- }
-
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- })
-
- http.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request){
- var verify Verify
- defer r.Body.Close()
-
- body, _ := ioutil.ReadAll(r.Body)
-
- err := json.Unmarshal(body, &verify)
-
- CheckError(err, "error get verify from json")
-
- v := GetVerificationByCode(db, verify.Code)
-
- if v.Identifier == verify.Identifier {
- w.Write([]byte(v.Board))
- return
- }
-
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- })
-
- fmt.Println("Server for " + Domain + " running on port " + Port)
-
- fmt.Println("Mod key: " + *Key)
- PrintAdminAuth(db)
-
- http.ListenAndServe(Port, nil)
-}
-
-func CheckError(e error, m string) error{
- if e != nil {
- fmt.Println()
- fmt.Println(m)
- fmt.Println()
- panic(e)
- }
-
- return e
-}
-
-func ConnectDB() *sql.DB {
-
- host := GetConfigValue("dbhost")
- port,_ := strconv.Atoi(GetConfigValue("dbport"))
- user := GetConfigValue("dbuser")
- password := GetConfigValue("dbpass")
- dbname := GetConfigValue("dbname")
-
- psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s " +
- "dbname=%s sslmode=disable", host, port, user, password, dbname)
-
- db, err := sql.Open("postgres", psqlInfo)
- CheckError(err, "error with db connection")
-
- err = db.Ping()
-
- CheckError(err, "error with db ping")
-
- fmt.Println("Successfully connected DB")
- return db
-}
-
-func CreateKey(len int) string {
- var key string
- str := (CreateTripCode(RandomID(len)))
- for i := 0; i < len; i++ {
- key += fmt.Sprintf("%c", str[i])
- }
- return key
-}
-
-func neuter(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if strings.HasSuffix(r.URL.Path, "/") {
- http.NotFound(w, r)
- return
- }
-
- next.ServeHTTP(w, r)
- })
-}
-
-func CreateTripCode(input string) string {
- cmd := exec.Command("sha512sum")
- cmd.Stdin = strings.NewReader(input)
- var out bytes.Buffer
- cmd.Stdout = &out
- err := cmd.Run()
-
- CheckError(err, "error with create trip code")
-
- code := strings.Split(out.String(), " ")
-
- return code[0]
-}
-
-func CreateNameTripCode(input string) string {
- re := regexp.MustCompile("#.+")
- chunck := re.FindString(input)
- hash := CreateTripCode(chunck)
- return re.ReplaceAllString(input, "!" + hash[42:50])
-}
-
-func GetActorFromPath(db *sql.DB, location string, prefix string) Actor {
- pattern := fmt.Sprintf("%s([^/\n]+)(/.+)?", prefix)
- re := regexp.MustCompile(pattern)
- match := re.FindStringSubmatch(location)
-
- var actor string
-
- if(len(match) < 1 ) {
- actor = "/"
- } else {
- actor = strings.Replace(match[1], "/", "", -1)
- }
-
- if actor == "/" || actor == "outbox" || actor == "inbox" || actor == "following" || actor == "followers" {
- actor = "main"
- }
-
- var nActor Actor
-
- nActor = GetActorByNameFromDB(db, actor)
-
- if nActor.Id == "" {
- nActor = GetActorByName(db, actor)
- }
-
- return nActor
-}
-
-func GetContentType(location string) string {
- elements := strings.Split(location, ";")
- if len(elements) > 0 {
- return elements[0]
- } else {
- return location
- }
-}
-
-func RandomID(size int) string {
- rand.Seed(time.Now().UnixNano())
- domain := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- rng := size
- newID := ""
- for i := 0; i < rng; i++ {
- newID += string(domain[rand.Intn(len(domain))])
- }
-
- return newID
-}
-
-func CreateUniqueID(db *sql.DB, actor string) string {
- var newID string
- isUnique := false
- for !isUnique {
- newID = RandomID(8)
-
- query := fmt.Sprintf("select id from activitystream where id='%s/%s/%s'", Domain, actor, newID)
-
- rows, err := db.Query(query)
-
- CheckError(err, "error with unique id query")
-
- defer rows.Close()
-
- var count int = 0
- for rows.Next(){
- count += 1
- }
-
- if count < 1 {
- isUnique = true
- }
- }
-
- return newID
-}
-
-func CreateNewActor(board string, prefName string, summary string, authReq []string, restricted bool) *Actor{
- actor := new(Actor)
-
- var path string
- if board == "" {
- path = Domain
- actor.Name = "main"
- } else {
- path = Domain + "/" + board
- actor.Name = board
- }
-
- actor.Type = "Service"
- actor.Id = fmt.Sprintf("%s", path)
- actor.Following = fmt.Sprintf("%s/following", actor.Id)
- actor.Followers = fmt.Sprintf("%s/followers", actor.Id)
- actor.Inbox = fmt.Sprintf("%s/inbox", actor.Id)
- actor.Outbox = fmt.Sprintf("%s/outbox", actor.Id)
- actor.PreferredUsername = prefName
- actor.Restricted = restricted
- actor.Summary = summary
- actor.AuthRequirement = authReq
-
- return actor
-}
-
-func GetActorInfo(w http.ResponseWriter, db *sql.DB, id string) {
- actor := GetActorFromDB(db, id)
- enc, _ := json.MarshalIndent(actor, "", "\t")
- w.Header().Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
- w.Write(enc)
-}
-
-func GetActorPost(w http.ResponseWriter, db *sql.DB, path string) {
- collection := GetCollectionFromPath(db, Domain + "" + path)
- if len(collection.OrderedItems) > 0 {
- enc, _ := json.MarshalIndent(collection, "", "\t")
- w.Header().Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
- w.Write(enc)
- }
-}
-
-func CreateObject(objType string) ObjectBase {
- var nObj ObjectBase
-
- nObj.Type = objType
- nObj.Published = time.Now().Format(time.RFC3339)
- nObj.Updated = time.Now().Format(time.RFC3339)
-
- return nObj
-}
-
-func AddFollowersToActivity(db *sql.DB, activity Activity) Activity{
-
- if len(activity.To) < 1 {
- activity.To = append(activity.To, activity.Actor.Id)
- }
-
- for _, e := range activity.To {
- aFollowers := GetActorCollection(e + "/followers")
- for _, k := range aFollowers.Items {
- activity.To = append(activity.To, k.Id)
- }
- }
-
- var nActivity Activity
-
- for _, e := range activity.To {
- var alreadyTo = false
- for _, k := range nActivity.To {
- if e == k || e == activity.Actor.Id {
- alreadyTo = true
- }
- }
-
- if !alreadyTo {
- nActivity.To = append(nActivity.To, e)
- }
- }
-
- activity.To = nActivity.To
-
- return activity
-}
-
-func CreateActivity(activityType string, obj ObjectBase) Activity {
- var newActivity Activity
-
- newActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- newActivity.Type = activityType
- newActivity.Published = obj.Published
- newActivity.Actor = obj.Actor
- newActivity.Object = &obj
-
- for _, e := range obj.To {
- if obj.Actor.Id != e {
- newActivity.To = append(newActivity.To, e)
- }
- }
-
- for _, e := range obj.Cc {
- if obj.Actor.Id != e {
- newActivity.Cc = append(newActivity.Cc, e)
- }
- }
-
- return newActivity
-}
-
-func ProcessActivity(db *sql.DB, activity Activity) {
- activityType := activity.Type
-
- if activityType == "Create" {
- for _, e := range activity.To {
- if GetActorFromDB(db, e).Id != "" {
- fmt.Println("actor is in the database")
- } else {
- fmt.Println("actor is NOT in the database")
- }
- }
- } else if activityType == "Follow" {
-
- } else if activityType == "Delete" {
-
- }
-}
-
-func CreatePreviewObject(obj ObjectBase) *NestedObjectBase {
-
- re := regexp.MustCompile(`/.+$`)
-
- mimetype := re.ReplaceAllString(obj.MediaType, "")
-
- var nPreview NestedObjectBase
-
- if mimetype != "image" {
- return &nPreview
- }
-
- re = regexp.MustCompile(`.+/`)
-
- file := re.ReplaceAllString(obj.MediaType, "")
-
- href := GetUniqueFilename(file)
-
- nPreview.Type = "Preview"
- nPreview.Name = obj.Name
- nPreview.Href = Domain + "" + href
- nPreview.MediaType = obj.MediaType
- nPreview.Size = obj.Size
- nPreview.Published = obj.Published
-
- re = regexp.MustCompile(`/public/.+`)
-
- objFile := re.FindString(obj.Href)
-
- cmd := exec.Command("convert", "." + objFile ,"-resize", "250x250>", "." + href)
-
- err := cmd.Run()
-
- if CheckError(err, "error with resize attachment preview") != nil {
- var preview NestedObjectBase
- return &preview
- }
-
- return &nPreview
-}
-
-func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]ObjectBase, *os.File) {
- contentType, _ := GetFileContentType(file)
- filename := header.Filename
- size := header.Size
-
- re := regexp.MustCompile(`.+/`)
-
- fileType := re.ReplaceAllString(contentType, "")
-
- tempFile, _ := ioutil.TempFile("./public", "*." + fileType)
-
- var nAttachment []ObjectBase
- var image ObjectBase
-
- image.Type = "Attachment"
- image.Name = filename
- image.Href = Domain + "/" + tempFile.Name()
- image.MediaType = contentType
- image.Size = size
- image.Published = time.Now().Format(time.RFC3339)
-
- nAttachment = append(nAttachment, image)
-
- return nAttachment, tempFile
-}
-
-func ParseCommentForReplies(comment string) []ObjectBase {
-
- re := regexp.MustCompile("(>>)(https://|http://)?(www\\.)?.+\\/\\w+")
- match := re.FindAllStringSubmatch(comment, -1)
-
- var links []string
-
- for i:= 0; i < len(match); i++ {
- str := strings.Replace(match[i][0], ">>", "", 1)
- str = strings.Replace(str, "www.", "", 1)
- str = strings.Replace(str, "http://", "", 1)
- str = strings.Replace(str, "https://", "", 1)
- str = TP + "" + str
- if !IsInStringArray(links, str) {
- links = append(links, str)
- }
- }
-
- var validLinks []ObjectBase
- for i:= 0; i < len(links); i++ {
- _, isValid := CheckValidActivity(links[i])
- if(isValid) {
- var reply = new(ObjectBase)
- reply.Id = links[i]
- reply.Published = time.Now().Format(time.RFC3339)
- validLinks = append(validLinks, *reply)
- }
- }
-
- return validLinks
-}
-
-func CheckValidActivity(id string) (Collection, bool) {
-
- req, err := http.NewRequest("GET", id, nil)
-
- if err != nil {
- fmt.Println("error with request")
- panic(err)
- }
-
- req.Header.Set("Accept", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- if err != nil {
- fmt.Println("error with response")
- panic(err)
- }
-
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- var respCollection Collection
-
- err = json.Unmarshal(body, &respCollection)
-
- if err != nil {
- panic(err)
- }
-
- if respCollection.AtContext.Context == "https://www.w3.org/ns/activitystreams" && respCollection.OrderedItems[0].Id != "" {
- return respCollection, true;
- }
-
- return respCollection, false;
-}
-
-func GetActor(id string) Actor {
-
- var respActor Actor
-
- if id == "" {
- return respActor
- }
-
- req, err := http.NewRequest("GET", id, nil)
-
- CheckError(err, "error with getting actor req")
-
- req.Header.Set("Accept", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- if err != nil {
- fmt.Println("error with getting actor resp " + id)
- return respActor
- }
-
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- err = json.Unmarshal(body, &respActor)
-
- CheckError(err, "error getting actor from body")
-
- return respActor
-}
-
-func GetActorCollection(collection string) Collection {
- var nCollection Collection
-
- if collection == "" {
- return nCollection
- }
-
- req, err := http.NewRequest("GET", collection, nil)
-
- CheckError(err, "error with getting actor collection req " + collection)
-
- req.Header.Set("Accept", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- if err != nil {
- fmt.Println("error with getting actor collection resp " + collection)
- return nCollection
- }
-
-
- defer resp.Body.Close()
-
- if resp.StatusCode == 200 {
- body, _ := ioutil.ReadAll(resp.Body)
-
- if len(body) > 0 {
- err = json.Unmarshal(body, &nCollection)
-
- CheckError(err, "error getting actor collection from body " + collection)
- }
- }
-
- return nCollection
-}
-
-func IsValidActor(id string) (Actor, bool) {
- var respCollection Actor
- req, err := http.NewRequest("GET", id, nil)
-
- CheckError(err, "error with valid actor request")
-
- req.Header.Set("Accept", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- CheckError(err, "error with valid actor response")
-
- defer resp.Body.Close()
-
- if resp.StatusCode == 403 {
- return respCollection, false;
- }
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- err = json.Unmarshal(body, &respCollection)
-
- if err != nil {
- panic(err)
- }
-
- if respCollection.Id != "" && respCollection.Inbox != "" && respCollection.Outbox != "" {
- return respCollection, true;
- }
-
- return respCollection, false;
-}
-
-func IsActivityLocal(db *sql.DB, activity Activity) bool {
- for _, e := range activity.To {
- if GetActorFromDB(db, e).Id != "" {
- return true
- }
- }
-
- for _, e := range activity.Cc {
- if GetActorFromDB(db, e).Id != "" {
- return true
- }
- }
-
- if activity.Actor != nil && GetActorFromDB(db, activity.Actor.Id).Id != "" {
- return true
- }
-
- return false
-}
-
-func IsIDLocal(db *sql.DB, id string) bool {
- activity := GetActivityFromDB(db, id)
- return len(activity.OrderedItems) > 0
-}
-
-func IsActorLocal(db *sql.DB, id string) bool {
- actor := GetActorFromDB(db, id)
-
- if actor.Id != "" {
- return true
- }
-
- return false
-}
-
-func IsObjectLocal(db *sql.DB, id string) bool {
-
- query := `select id from activitystream where id=$1`
-
- rows, _ := db.Query(query, id)
-
- var nID string
- defer rows.Close()
- rows.Next()
- rows.Scan(&nID)
-
- if nID == "" {
- return false
- }
-
- return true
-}
-
-func IsObjectCached(db *sql.DB, id string) bool {
-
- query := `select id from cacheactivitystream where id=$1`
- rows, _ := db.Query(query, id)
-
- var nID string
- defer rows.Close()
- rows.Next()
- rows.Scan(&nID)
-
- if nID == "" {
- return false
- }
-
- return true
-}
-
-func GetObjectFromActivity(activity Activity) ObjectBase {
- return *activity.Object
-}
-
-func MakeCaptchas(db *sql.DB, total int) {
- difference := total - GetCaptchaTotal(db)
-
- for i := 0; i < difference; i++ {
- CreateNewCaptcha(db)
- }
-}
-
-func GetFileContentType(out multipart.File) (string, error) {
-
- buffer := make([]byte, 512)
-
- _, err := out.Read(buffer)
- if err != nil {
- return "", err
- }
-
- out.Seek(0, 0)
-
- contentType := http.DetectContentType(buffer)
-
- return contentType, nil
-}
-
-func IsReplyInThread(db *sql.DB, inReplyTo string, id string) bool {
- obj, _ := CheckValidActivity(inReplyTo)
-
- for _, e := range obj.OrderedItems[0].Replies.OrderedItems {
- if e.Id == id {
- return true
- }
- }
- return false
-}
-
-func SupportedMIMEType(mime string) bool {
- for _, e := range supportedFiles {
- if e == mime {
- return true
- }
- }
-
- return false
-}
-
-func DeleteReportActivity(db *sql.DB, id string) bool {
-
- query := `delete from reported where id=$1`
-
- _, err := db.Exec(query, id)
-
- if err != nil {
- CheckError(err, "error closing reported activity")
- return false
- }
-
- return true
-}
-
-func ReportActivity(db *sql.DB, id string, reason string) bool {
-
- if !IsIDLocal(db, id) {
- return false
- }
-
- actor := GetActivityFromDB(db, id)
-
- query := `select count from reported where id=$1`
-
- rows, err := db.Query(query, id)
-
- CheckError(err, "could not select count from reported")
-
- defer rows.Close()
- var count int
- for rows.Next() {
- rows.Scan(&count)
- }
-
- if count < 1 {
- query = `insert into reported (id, count, board, reason) values ($1, $2, $3, $4)`
-
- _, err := db.Exec(query, id, 1, actor.Actor.Id, reason)
-
- if err != nil {
- CheckError(err, "error inserting new reported activity")
- return false
- }
-
- } else {
- count = count + 1
- query = `update reported set count=$1 where id=$2`
-
- _, err := db.Exec(query, count, id)
-
- if err != nil {
- CheckError(err, "error updating reported activity")
- return false
- }
- }
-
- return true
-}
-
-func GetActorReported(w http.ResponseWriter, r *http.Request, db *sql.DB, id string) {
-
- auth := r.Header.Get("Authorization")
- verification := strings.Split(auth, " ")
-
- if len(verification) < 2 {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- return
- }
-
- if !HasAuth(db, verification[1], id) {
- w.WriteHeader(http.StatusBadRequest)
- w.Write([]byte(""))
- return
- }
-
- var following Collection
-
- following.AtContext.Context = "https://www.w3.org/ns/activitystreams"
- following.Type = "Collection"
- following.TotalItems = GetActorReportedTotal(db, id)
- following.Items = GetActorReportedDB(db, id)
-
- enc, _ := json.MarshalIndent(following, "", "\t")
- w.Header().Set("Content-Type", activitystreams)
- w.Write(enc)
-}
-
-func MakeActivityRequest(db *sql.DB, activity Activity) {
-
- j, _ := json.MarshalIndent(activity, "", "\t")
-
- var verify Verify
-
- verify.Board = activity.Actor.Id
- verify.Identifier = "post"
-
- verify = GetVerificationCode(db, verify)
-
- auth := CreateTripCode(verify.Code)
-
- auth = CreateTripCode(auth)
-
- for _, e := range activity.To {
-
- actor := GetActor(e)
-
- if actor.Inbox != "" {
- req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j))
-
- req.Header.Set("Content-Type", activitystreams)
-
- req.Header.Set("Authorization", "Basic " + auth)
-
- CheckError(err, "error with sending activity req to")
-
- _, err = http.DefaultClient.Do(req)
-
- CheckError(err, "error with sending activity resp to")
- }
- }
-}
-
-func GetCollectionFromID(id string) Collection {
- var nColl Collection
-
- req, err := http.NewRequest("GET", id, nil)
-
- CheckError(err, "could not get collection from id req")
-
- req.Header.Set("Accept", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- if err != nil {
- CheckError(err, "could not get collection from " + id)
- return nColl
- }
-
- if resp.StatusCode == 200 {
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- err = json.Unmarshal(body, &nColl)
-
- CheckError(err, "error getting collection resp from json body")
-
- }
-
- return nColl
-}
-
-func GetActorFromID(id string) Actor {
- req, err := http.NewRequest("GET", id, nil)
-
- CheckError(err, "error getting actor from id req")
-
- req.Header.Set("Accept", activitystreams)
-
- resp, err := http.DefaultClient.Do(req)
-
- CheckError(err, "error getting actor from id resp")
-
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- var respCollection Collection
-
- err = json.Unmarshal(body, &respCollection)
-
- CheckError(err, "error getting actor resp from json body")
-
- return *respCollection.OrderedItems[0].Actor
-}
-
-func GetConfigValue(value string) string{
- file, err := os.Open("config")
-
- CheckError(err, "there was an error opening the config file")
-
- defer file.Close()
-
- lines := bufio.NewScanner(file)
-
- for lines.Scan() {
- line := strings.SplitN(lines.Text(), ":", 2)
- if line[0] == value {
- return line[1]
- }
- }
-
- return ""
-}
-
-func PrintAdminAuth(db *sql.DB){
- query := fmt.Sprintf("select identifier, code from boardaccess where board='%s' and type='admin'", Domain)
-
- rows, err := db.Query(query)
-
- CheckError(err, "Error getting Domain auth")
-
- var code string
- var identifier string
-
- rows.Next()
- rows.Scan(&identifier, &code)
-
- fmt.Println("Admin Login: " + identifier + ", Code: " + code)
-}
-
-func IsInStringArray(array []string, value string) bool {
- for _, e := range array {
- if e == value {
- return true
- }
- }
- return false
-}
-
-func GetUniqueFilename(_type string) string {
- id := RandomID(8)
- file := "/public/" + id + "." + _type
-
- for true {
- if _, err := os.Stat("." + file); err == nil {
- id = RandomID(8)
- file = "/public/" + id + "." + _type
- }else{
- return "/public/" + id + "." + _type
- }
- }
-
- return ""
-}
-
-func DeleteObjectRequest(db *sql.DB, id string) {
- var nObj ObjectBase
- var nActor Actor
- nObj.Id = id
- nObj.Actor = &nActor
-
- activity := CreateActivity("Delete", nObj)
-
- obj := GetObjectFromPath(db, id)
-
- activity.Actor.Id = obj.Actor.Id
-
- followers := GetActorFollowDB(db, obj.Actor.Id)
- for _, e := range followers {
- activity.To = append(activity.To, e.Id)
- }
-
- following := GetActorFollowingDB(db, obj.Actor.Id)
- for _, e := range following {
- activity.To = append(activity.To, e.Id)
- }
-
- MakeActivityRequest(db, activity)
-}
-
-func DeleteObjectAndRepliesRequest(db *sql.DB, id string) {
- var nObj ObjectBase
- var nActor Actor
- nObj.Id = id
- nObj.Actor = &nActor
-
- activity := CreateActivity("Delete", nObj)
-
- obj := GetObjectByIDFromDB(db, id)
-
- activity.Actor.Id = obj.OrderedItems[0].Actor.Id
-
- activity.Object = &obj.OrderedItems[0]
-
- followers := GetActorFollowDB(db, obj.OrderedItems[0].Actor.Id)
- for _, e := range followers {
- activity.To = append(activity.To, e.Id)
- }
-
- following := GetActorFollowingDB(db, obj.OrderedItems[0].Actor.Id)
- for _, e := range following {
- activity.To = append(activity.To, e.Id)
- }
-
- MakeActivityRequest(db, activity)
-}
-
-func ResizeAttachmentToPreview(db *sql.DB) {
- query := `select id, href, mediatype, name, size, published from activitystream where id in (select attachment from activitystream where attachment!='' and preview='')`
-
- rows, err := db.Query(query)
-
- CheckError(err, "error getting attachments")
-
-
- defer rows.Close()
- for rows.Next() {
-
- var id string
- var href string
- var mediatype string
- var name string
- var size int
- var published string
-
- rows.Scan(&id, &href, &mediatype, &name, &size, &published)
-
- re := regexp.MustCompile(`^\w+`)
-
- _type := re.FindString(mediatype)
-
- if _type == "image" {
-
- re = regexp.MustCompile(`.+/`)
-
- file := re.ReplaceAllString(mediatype, "")
-
- nHref := GetUniqueFilename(file)
-
- var nPreview NestedObjectBase
-
- re = regexp.MustCompile(`/\w+$`)
- actor := re.ReplaceAllString(id, "")
-
- nPreview.Type = "Preview"
- nPreview.Id = fmt.Sprintf("%s/%s", actor, CreateUniqueID(db, actor))
- nPreview.Name = name
- nPreview.Href = Domain + "" + nHref
- nPreview.MediaType = mediatype
- nPreview.Size = int64(size)
- nPreview.Published = published
- nPreview.Updated = published
-
- re = regexp.MustCompile(`/public/.+`)
-
- objFile := re.FindString(href)
-
- if(id != "") {
- cmd := exec.Command("convert", "." + objFile ,"-resize", "250x250>", "." + nHref)
-
- err := cmd.Run()
-
- CheckError(err, "error with resize attachment preview")
-
- if err == nil {
- fmt.Println(objFile + " -> " + nHref)
- WritePreviewToDB(db, nPreview)
- UpdateObjectWithPreview(db, id, nPreview.Id)
- }
- }
- }
- }
-}
-
-func UpdateObjectWithPreview(db *sql.DB, id string, preview string) {
- query := `update activitystream set preview=$1 where attachment=$2`
-
- _, err := db.Exec(query, preview, id)
-
- CheckError(err, "could not update activity stream with preview")
-
-}
-
-func ParseCommentForReply(comment string) string {
-
- re := regexp.MustCompile("(>>)(https://|http://)?(www\\.)?.+\\/\\w+")
- match := re.FindAllStringSubmatch(comment, -1)
-
- var links []string
-
- for i:= 0; i < len(match); i++ {
- str := strings.Replace(match[i][0], ">>", "", 1)
- links = append(links, str)
- }
-
- if(len(links) > 0){
- _, isValid := CheckValidActivity(links[0])
-
- if(isValid) {
- return links[0]
- }
- }
-
- return ""
-}
-
-func GetActorByName(db *sql.DB, name string) Actor {
- var actor Actor
- for _, e := range Boards {
- if e.Actor.Name == name {
- actor = e.Actor
- }
- }
-
- return actor
-}
-
-func GetActorCollectionReq(r *http.Request, collection string) Collection {
- var nCollection Collection
-
- req, err := http.NewRequest("GET", collection, nil)
-
- CheckError(err, "error with getting actor collection req " + collection)
-
- _, pass := GetPasswordFromSession(r)
-
- req.Header.Set("Accept", activitystreams)
-
- req.Header.Set("Authorization", "Basic " + pass)
-
- resp, err := http.DefaultClient.Do(req)
-
- CheckError(err, "error with getting actor collection resp " + collection)
-
- if resp.StatusCode == 200 {
-
- defer resp.Body.Close()
-
- body, _ := ioutil.ReadAll(resp.Body)
-
- err = json.Unmarshal(body, &nCollection)
-
- CheckError(err, "error getting actor collection from body " + collection)
- }
-
- return nCollection
-}
-
-
-func shortURL(actorName string, url string) string {
-
- re := regexp.MustCompile(`outbox`)
-
- actor := re.ReplaceAllString(actorName, "")
-
- re = regexp.MustCompile(`\w+$`)
- temp := re.ReplaceAllString(url, "")
-
- if(temp == actor){
- short := StripTransferProtocol(url)
-
- re := regexp.MustCompile(`\w+$`)
-
- id := re.FindString(short);
-
- return id;
- }else{
- short := StripTransferProtocol(url)
-
- re := regexp.MustCompile(`\w+$`)
-
- id := re.FindString(short);
-
- re = regexp.MustCompile(`.+/.+/`)
-
- actorurl := re.FindString(short)
-
- re = regexp.MustCompile(`/.+/`)
-
- actorname := re.FindString(actorurl)
-
- actorname = strings.Replace(actorname, "/", "", -1)
-
- id = "f" + actorname + "-" + id
-
- return id;
- }
-}
diff --git a/README.md b/README.md
index 1e2621a..e497144 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,27 @@
# About
-FChannel is a [libre](https://en.wikipedia.org/wiki/Free_and_open-source_software), [self-hostable](https://en.wikipedia.org/wiki/Self-hosting_(web_services)), [federated](https://en.wikipedia.org/wiki/Federation_(information_technology)), [imageboard](https://en.wikipedia.org/wiki/Imageboard) platform that utilizes [ActivityPub](https://activitypub.rocks/).
+FChannel is a [libre](https://en.wikipedia.org/wiki/Free_and_open-source_software), [self-hostable](https://en.wikipedia.org/wiki/Self-hosting_(web_services)), [federated](https://en.wikipedia.org/wiki/Federation_(information_technology)), [imageboard](https://en.wikipedia.org/wiki/Imageboard) platform that utilizes [ActivityPub](https://activitypub.rocks/).
+
+There are currently two instances federated with each other: https://fchan.xyz and https://0x00000000.xyz
+
+There is an anon testing FChannel instances on tor/loki/i2p, find more information here: https://fchan.xyz/g/MORL0KUT
+It is a testing envirmoent, so the instances might come and go.
+
+Current things that will be implemented first are a way to automatically index new instances into a list so others can discover instances as they come online. And setting up a server proxy so that clearnet instances can talk to tor/loki/i2p instances. Other improvements will be made over time, first it needs to be as easy as possible for new instances to come online and connect with others reliably.
+
+Try and run your own instances and federate with one of the instances above.
+
+Any contributions or suggestions are appreciated. Best way to give immediate feedback is the matrix channel #fchan:matrix.org
# Server Installation and Configuration
## Minimum Server Requirements
-_POST MINIMUM SERVER REQUIREMENTS HERE (OS, Hardware, etc.) HERE_
+- golang v1.11+
+
+- postgresql
+
+- redis
## Server Installation Instructions
@@ -14,7 +29,7 @@ _POST MINIMUM SERVER REQUIREMENTS HERE (OS, Hardware, etc.) HERE_
- Copy `config-init` to `config` and change the values appropriately to reflect the instance.
-- Create the database, username, and password for psql that is used in config file.
+- Create the database, user name, and password for psql that is used in config file.
- Run `psql -U (user) -d (database) -f databaseschema.psql`
@@ -22,22 +37,110 @@ _POST MINIMUM SERVER REQUIREMENTS HERE (OS, Hardware, etc.) HERE_
## Server Configuration
-_POST VARIOUS NOTABLE CONFIG OPTIONS THE HOST MAY WANT TO CHANGE IN CONFIGURATION, AS WELL AS ANY SECURITY SETTINGS HERE._
+### config file
+
+ `instance:fchan.xyz` Domain name that the host can be located at without www and http:// or https://
+
+ `instancetp:https://` Transfer protocol your domain is using, should be https if possible, do not put https:// if it is http:// you are using
+
+ `instanceport:3000` Port your server is running on locally on your server
+
+ `instancename:FChan` Full name that you want your instances to be called
+
+ `instancesummary:FChan is a federated image board instance.` Brief description of your instance
+
+
+ `dbhost:localhost` Database host, most likely leave to localhost
+
+ `dbport:5432` Port number for database, most likely leave default
+
+ `dbname:fchan_server` Database name for psql
+
+ `dbuser:admin` Database user that can connect to dbname
+
+ `dbpass:password` Database password for dbuser
+
+
+ Currently email is not hooked up to do anything special, but code is in place
+
+ `emailserver:mail.fchan.xyz`
+
+ `emailport:465`
+
+ `emailaddress:contact@fchan.xyz`
+
+ `emailpass:password`
+
+
+### 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
-_PROVIDE INSTRUCTIONS FOR UPDATING THE SERVER TO THE LATEST VERSION HERE._
+ Check the git repo for latest commits. If there are commits you want to update to, pull and restart instance.
## Networking
### NGINX Template
-_PROVIDE A BASIC NGINX TEMPLATE TWEAKED FOR FCHANNEL HERE_
+Use certbot to setup ssl
+
+```
+server {
+ listen 80;
+ listen [::]:80;
+
+ root /var/www/html;
+
+ server_name fchan.xyz www.fchan.xyz;
+
+ client_max_body_size 100M;
+
+ location / {
+ # First attempt to serve request as file, then
+ # as directory, then fall back to displaying a 404.
+ #try_files $uri $uri/ =404;
+ proxy_pass http://localhost:3000;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_cache_bypass $http_upgrade;
+ }
+}
+```
### Apache
-_PROVIDE A BASIC APACHE TEMPLATE TWEAKED FOR FCHANNEL HERE_
+`Please consider submitting a pull request if you set up a FChannel instance with Apache with instructions on how to do so`
### Caddy
-_PROVIDE A BASIC CADDYv2 TEMPLATE TWEAKED FOR FCHANNEL HERE_
+`Please consider submitting a pull request if you set up a FChannel instance with Caddy with instructions on how to do so`
+
+### Docker
+
+`Please consider submitting a pull request if you set up a FChannel instance with Docker with instructions on how to do so` \ No newline at end of file