diff options
author | FChannel <> | 2022-04-30 11:00:55 -0700 |
---|---|---|
committer | FChannel <> | 2022-06-19 12:53:29 -0700 |
commit | 1892327cee2c3fa1d3bea729bd08eb63c2189a96 (patch) | |
tree | 7b846f7d9caf46fba6c9d15ff81b9d89dcca9476 | |
parent | 5b52d269faa2ce2014d0feba603a2122361cf4eb (diff) |
restructured code base to prevent circular dependicies
-rw-r--r-- | activitypub/activity.go | 524 | ||||
-rw-r--r-- | activitypub/actor.go | 853 | ||||
-rw-r--r-- | activitypub/object.go | 1802 | ||||
-rw-r--r-- | activitypub/pem.go | 241 | ||||
-rw-r--r-- | client.go | 2 | ||||
-rw-r--r-- | config/config.go | 2 | ||||
-rw-r--r-- | db/actor.go | 67 | ||||
-rw-r--r-- | db/cache.go | 268 | ||||
-rw-r--r-- | db/database.go | 2020 | ||||
-rw-r--r-- | db/follow.go | 238 | ||||
-rw-r--r-- | db/local.go | 42 | ||||
-rw-r--r-- | db/outbox.go | 116 | ||||
-rw-r--r-- | db/pem.go | 310 | ||||
-rw-r--r-- | db/report.go | 75 | ||||
-rw-r--r-- | db/verification.go | 43 | ||||
-rw-r--r-- | main.go | 283 | ||||
-rw-r--r-- | outboxGet.go | 10 | ||||
-rw-r--r-- | outboxPost.go | 400 | ||||
-rw-r--r-- | post/util.go (renamed from db/blacklist.go) | 67 | ||||
-rw-r--r-- | routes/admin.go | 5 | ||||
-rw-r--r-- | routes/archive.go | 5 | ||||
-rw-r--r-- | routes/index.go | 10 | ||||
-rw-r--r-- | routes/news.go | 10 | ||||
-rw-r--r-- | routes/outbox.go | 10 | ||||
-rw-r--r-- | routes/post.go | 21 | ||||
-rw-r--r-- | routes/structs.go | 12 | ||||
-rw-r--r-- | routes/util.go | 12 | ||||
-rw-r--r-- | util/accept.go | 21 | ||||
-rw-r--r-- | util/util.go | 80 | ||||
-rw-r--r-- | webfinger/util.go (renamed from db/boards.go) | 53 | ||||
-rw-r--r-- | webfinger/webfinger.go | 4 |
31 files changed, 3813 insertions, 3793 deletions
diff --git a/activitypub/activity.go b/activitypub/activity.go new file mode 100644 index 0000000..aef060b --- /dev/null +++ b/activitypub/activity.go @@ -0,0 +1,524 @@ +package activitypub + +import ( + "crypto" + crand "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" + + "github.com/FChannel0/FChannel-Server/config" + "github.com/FChannel0/FChannel-Server/util" + "github.com/gofiber/fiber/v2" +) + +// False positive for application/ld+ld, application/activity+ld, application/json+json +var activityRegexp = regexp.MustCompile("application\\/(ld|json|activity)((\\+(ld|json))|$)") + +func AcceptActivity(header string) bool { + accept := false + if strings.Contains(header, ";") { + split := strings.Split(header, ";") + accept = accept || activityRegexp.MatchString(split[0]) + accept = accept || strings.Contains(split[len(split)-1], "profile=\"https://www.w3.org/ns/activitystreams\"") + } else { + accept = accept || activityRegexp.MatchString(header) + } + return accept +} + +func ActivitySign(actor Actor, signature string) (string, error) { + query := `select file from publicKeyPem where id=$1 ` + + rows, err := config.DB.Query(query, actor.PublicKey.Id) + if err != nil { + return "", err + } + + defer rows.Close() + + var file string + rows.Next() + rows.Scan(&file) + + file = strings.ReplaceAll(file, "public.pem", "private.pem") + _, err = os.Stat(file) + if err != nil { + fmt.Println(`\n Unable to locate private key. Now, +this means that you are now missing the proof that you are the +owner of the "` + actor.Name + `" board. If you are the developer, +then your job is just as easy as generating a new keypair, but +if this board is live, then you'll also have to convince the other +owners to switch their public keys for you so that they will start +accepting your posts from your board from this site. Good luck ;)`) + return "", errors.New("unable to locate private key") + } + + publickey, err := ioutil.ReadFile(file) + if err != nil { + return "", err + } + + block, _ := pem.Decode(publickey) + + pub, _ := x509.ParsePKCS1PrivateKey(block.Bytes) + rng := crand.Reader + hashed := sha256.New() + hashed.Write([]byte(signature)) + cipher, _ := rsa.SignPKCS1v15(rng, pub, crypto.SHA256, hashed.Sum(nil)) + + return base64.StdEncoding.EncodeToString(cipher), nil +} + +func DeleteReportActivity(id string) error { + query := `delete from reported where id=$1` + + _, err := config.DB.Exec(query, id) + return err +} + +func GetActivityFromDB(id string) (Collection, error) { + var nColl Collection + var nActor Actor + var result []ObjectBase + + nColl.Actor = &nActor + + query := `select actor, id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 order by updated asc` + + rows, err := config.DB.Query(query, id) + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&nColl.Actor.Id, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + var err error + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetActivityFromJson(ctx *fiber.Ctx) (Activity, error) { + + var respActivity ActivityRaw + var nActivity Activity + var nType string + + if err := json.Unmarshal(ctx.Body(), &respActivity); err != nil { + return nActivity, err + } + + if res, err := HasContextFromJson(respActivity.AtContextRaw.Context); err == nil && res { + var jObj ObjectBase + + if respActivity.Type == "Note" { + jObj, err = GetObjectFromJson(ctx.Body()) + if err != nil { + return nActivity, err + } + + nType = "Create" + } else { + jObj, err = GetObjectFromJson(respActivity.ObjectRaw) + if err != nil { + return nActivity, err + } + + nType = respActivity.Type + } + + actor, err := GetActorFromJson(respActivity.ActorRaw) + if err != nil { + return nActivity, err + } + + to, err := GetToFromJson(respActivity.ToRaw) + if err != nil { + return nActivity, err + } + + cc, err := GetToFromJson(respActivity.CcRaw) + if err != nil { + return nActivity, err + } + + nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" + nActivity.Type = nType + nActivity.Actor = &actor + nActivity.Published = respActivity.Published + nActivity.Auth = respActivity.Auth + + if len(to) > 0 { + nActivity.To = to + } + + if len(cc) > 0 { + nActivity.Cc = cc + } + + nActivity.Name = respActivity.Name + nActivity.Object = &jObj + } else if err != nil { + return nActivity, err + } + + return nActivity, nil +} + +func HasContextFromJson(context []byte) (bool, error) { + var generic interface{} + + err := json.Unmarshal(context, &generic) + if err != nil { + return false, err + } + + hasContext := false + + switch generic.(type) { + case []interface{}: + var arrContext AtContextArray + err = json.Unmarshal(context, &arrContext.Context) + if len(arrContext.Context) > 0 { + if arrContext.Context[0] == "https://www.w3.org/ns/activitystreams" { + hasContext = true + } + } + break + + case string: + var arrContext AtContextString + err = json.Unmarshal(context, &arrContext.Context) + if arrContext.Context == "https://www.w3.org/ns/activitystreams" { + hasContext = true + } + break + } + + return hasContext, err +} + +func IsActivityLocal(activity Activity) (bool, error) { + for _, e := range activity.To { + if res, err := GetActorFromDB(e); err == nil && res.Id != "" { + return true, nil + } else if err != nil { + return false, err + } + } + + for _, e := range activity.Cc { + if res, err := GetActorFromDB(e); err == nil && res.Id != "" { + return true, nil + } else if err != nil { + return false, err + } + } + + if res, err := GetActorFromDB(activity.Actor.Id); err == nil && activity.Actor != nil && res.Id != "" { + return true, nil + } else if err != nil { + return false, err + } + + return false, nil +} + +func ProcessActivity(activity Activity) error { + activityType := activity.Type + + if activityType == "Create" { + for _, e := range activity.To { + if res, err := GetActorFromDB(e); err == nil && res.Id != "" { + fmt.Println("actor is in the database") + } else if err != nil { + return err + } else { + fmt.Println("actor is NOT in the database") + } + } + } else if activityType == "Follow" { + // TODO: okay? + return errors.New("not implemented") + } else if activityType == "Delete" { + return errors.New("not implemented") + } + + return nil +} + +func RejectActivity(activity Activity) Activity { + var accept Activity + accept.AtContext.Context = activity.AtContext.Context + accept.Type = "Reject" + var nObj ObjectBase + accept.Object = &nObj + var nActor Actor + accept.Actor = &nActor + accept.Actor.Id = activity.Object.Actor + accept.Object.Actor = activity.Actor.Id + var nNested NestedObjectBase + accept.Object.Object = &nNested + accept.Object.Object.Actor = activity.Object.Actor + accept.Object.Object.Type = "Follow" + accept.To = append(accept.To, activity.Actor.Id) + + return accept +} + +func ReportActivity(id string, reason string) (bool, error) { + if res, err := IsIDLocal(id); err == nil && !res { + // TODO: not local error + return false, nil + } else if err != nil { + return false, err + } + + actor, err := GetActivityFromDB(id) + if err != nil { + return false, err + } + + query := `select count from reported where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return false, err + } + defer rows.Close() + + var count int + for rows.Next() { + if err := rows.Scan(&count); err != nil { + return false, err + } + } + + if count < 1 { + query = `insert into reported (id, count, board, reason) values ($1, $2, $3, $4)` + + _, err := config.DB.Exec(query, id, 1, actor.Actor.Id, reason) + if err != nil { + return false, err + } + } else { + count = count + 1 + query = `update reported set count=$1 where id=$2` + + _, err := config.DB.Exec(query, count, id) + if err != nil { + return false, err + } + } + + return true, nil +} + +func WriteActivitytoCache(obj ObjectBase) error { + obj.Name = util.EscapeString(obj.Name) + obj.Content = util.EscapeString(obj.Content) + obj.AttributedTo = util.EscapeString(obj.AttributedTo) + + query := `select id from cacheactivitystream where id=$1` + + rows, err := config.DB.Query(query, obj.Id) + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + err = rows.Scan(&id) + if err != nil { + return err + } else if id != "" { + return nil // TODO: error? + } + + if obj.Updated.IsZero() { + obj.Updated = obj.Published + } + + query = `insert into cacheactivitystream (id, type, name, content, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` + + _, err = config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + return err +} + +func WriteActivitytoCacheWithAttachment(obj ObjectBase, attachment ObjectBase, preview NestedObjectBase) error { + obj.Name = util.EscapeString(obj.Name) + obj.Content = util.EscapeString(obj.Content) + obj.AttributedTo = util.EscapeString(obj.AttributedTo) + + query := `select id from cacheactivitystream where id=$1` + + rows, err := config.DB.Query(query, obj.Id) + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + err = rows.Scan(&id) + if err != nil { + return err + } else if id != "" { + return nil // TODO: error? + } + + if obj.Updated.IsZero() { + obj.Updated = obj.Published + } + + query = `insert into cacheactivitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` + + _, err = config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + return err +} + +func WriteActivitytoDB(obj ObjectBase) error { + obj.Name = util.EscapeString(obj.Name) + obj.Content = util.EscapeString(obj.Content) + obj.AttributedTo = util.EscapeString(obj.AttributedTo) + + query := `insert into activitystream (id, type, name, content, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` + + _, err := config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + return err +} + +func WriteActivitytoDBWithAttachment(obj ObjectBase, attachment ObjectBase, preview NestedObjectBase) { + + obj.Name = util.EscapeString(obj.Name) + obj.Content = util.EscapeString(obj.Content) + obj.AttributedTo = util.EscapeString(obj.AttributedTo) + + query := `insert into activitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` + + _, e := config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + + if e != nil { + fmt.Println("error inserting new activity with attachment") + panic(e) + } +} + +func WriteAttachmentToCache(obj ObjectBase) error { + query := `select id from cacheactivitystream where id=$1` + + rows, err := config.DB.Query(query, obj.Id) + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + err = rows.Scan(&id) + if err != nil { + return err + } else if id != "" { + return nil // TODO: error? + } + + if obj.Updated.IsZero() { + obj.Updated = obj.Published + } + + query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, err = config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) + return err +} + +func WriteAttachmentToDB(obj ObjectBase) { + query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, e := config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) + + if e != nil { + fmt.Println("error inserting new attachment") + panic(e) + } +} + +func WritePreviewToCache(obj NestedObjectBase) error { + query := `select id from cacheactivitystream where id=$1` + + rows, err := config.DB.Query(query, obj.Id) + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + err = rows.Scan(&id) + if err != nil { + return err + } else if id != "" { + return nil // TODO: error? + } + + if obj.Updated.IsZero() { + obj.Updated = obj.Published + } + + query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, err = config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) + return err +} + +func WritePreviewToDB(obj NestedObjectBase) error { + query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` + + _, err := config.DB.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) + return err +} diff --git a/activitypub/actor.go b/activitypub/actor.go new file mode 100644 index 0000000..d9399ab --- /dev/null +++ b/activitypub/actor.go @@ -0,0 +1,853 @@ +package activitypub + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "regexp" + "strings" + + "github.com/FChannel0/FChannel-Server/config" + "github.com/FChannel0/FChannel-Server/post" + "github.com/FChannel0/FChannel-Server/util" + "github.com/gofiber/fiber/v2" +) + +func CreateNewActor(board string, prefName string, summary string, authReq []string, restricted bool) *Actor { + actor := new(Actor) + + var path string + if board == "" { + path = config.Domain + actor.Name = "main" + } else { + path = config.Domain + "/" + board + actor.Name = board + } + + actor.Type = "Group" + 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 DeleteActorCache(actorID string) error { + query := `select id from cacheactivitystream where id in (select id from cacheactivitystream where actor=$1)` + + rows, err := config.DB.Query(query, actorID) + + if err != nil { + return err + } + + defer rows.Close() + + for rows.Next() { + var id string + if err := rows.Scan(&id); err != nil { + return err + } + + if err := DeleteObject(id); err != nil { + return err + } + } + + return nil +} + +func GetActorAuth(actor string) ([]string, error) { + var auth []string + + query := `select type from actorauth where board=$1` + + rows, err := config.DB.Query(query, actor) + if err != nil { + return auth, err + } + defer rows.Close() + + for rows.Next() { + var e string + if err := rows.Scan(&e); err != nil { + return auth, err + } + + auth = append(auth, e) + } + + return auth, nil +} + +func GetActorAutoSubscribeDB(id string) (bool, error) { + query := `select autosubscribe from actor where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return false, err + } + + var subscribed bool + defer rows.Close() + rows.Next() + err = rows.Scan(&subscribed) + return subscribed, err +} + +func GetActorByNameFromDB(name string) (Actor, error) { + var nActor Actor + + query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1` + + rows, err := config.DB.Query(query, name) + if err != nil { + return nActor, err + } + + var publicKeyPem string + defer rows.Close() + for rows.Next() { + if err := rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem); err != nil { + return nActor, err + } + } + + if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { + if err := CreatePublicKeyFromPrivate(&nActor, publicKeyPem); err != nil { + return nActor, err + } + } + + return nActor, nil +} + +func GetActorCollectionDBType(actorId string, nType string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc` + + rows, err := config.DB.Query(query, actorId, nType) + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var replies CollectionBase + + post.Replies = &replies + + var err error + post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) + if err != nil { + return nColl, err + } + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetActorCollectionDBTypeLimit(actorId string, nType string, limit int) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc limit $3` + + rows, err := config.DB.Query(query, actorId, nType, limit) + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var replies CollectionBase + + post.Replies = &replies + + var err error + post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) + if err != nil { + return nColl, err + } + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetActorCollectionReq(collection string) (Collection, error) { + var nCollection Collection + + req, err := http.NewRequest("GET", collection, nil) + if err != nil { + return nCollection, err + } + + // TODO: rewrite this for fiber + pass := "FIXME" + //_, pass := GetPasswordFromSession(r) + + req.Header.Set("Accept", config.ActivityStreams) + + req.Header.Set("Authorization", "Basic "+pass) + + resp, err := util.RouteProxy(req) + if err != nil { + return nCollection, err + } + defer resp.Body.Close() + + if resp.StatusCode == 200 { + body, _ := ioutil.ReadAll(resp.Body) + + if err := json.Unmarshal(body, &nCollection); err != nil { + return nCollection, err + } + } + + return nCollection, nil +} + +func GetActorFollowDB(id string) ([]ObjectBase, error) { + var followerCollection []ObjectBase + + query := `select follower from follower where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return followerCollection, err + } + defer rows.Close() + + for rows.Next() { + var obj ObjectBase + + if err := rows.Scan(&obj.Id); err != nil { + return followerCollection, err + } + + followerCollection = append(followerCollection, obj) + } + + return followerCollection, nil +} + +func GetActorFollowNameFromPath(path string) string { + var actor string + + re := regexp.MustCompile("f\\w+-") + + actor = re.FindString(path) + + actor = strings.Replace(actor, "f", "", 1) + actor = strings.Replace(actor, "-", "", 1) + + return actor +} + +func GetActorFollowTotal(id string) (int, int, error) { + var following int + var followers int + + query := `select count(following) from following where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return 0, 0, err + } + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(&following); err != nil { + return following, 0, err + } + } + + query = `select count(follower) from follower where id=$1` + + rows, err = config.DB.Query(query, id) + if err != nil { + return 0, 0, err + } + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(&followers); err != nil { + return following, followers, err + } + + } + + return following, followers, nil +} + +func GetActorFollowers(w http.ResponseWriter, id string) error { + var following Collection + var err error + + following.AtContext.Context = "https://www.w3.org/ns/activitystreams" + following.Type = "Collection" + _, following.TotalItems, err = GetActorFollowTotal(id) + if err != nil { + return err + } + + following.Items, err = GetActorFollowDB(id) + if err != nil { + return err + } + + enc, _ := json.MarshalIndent(following, "", "\t") + w.Header().Set("Content-Type", config.ActivityStreams) + _, err = w.Write(enc) + return err +} + +func GetActorFollowing(w http.ResponseWriter, id string) error { + var following Collection + var err error + + following.AtContext.Context = "https://www.w3.org/ns/activitystreams" + following.Type = "Collection" + following.TotalItems, _, err = GetActorFollowTotal(id) + if err != nil { + return err + } + + following.Items, err = GetActorFollowingDB(id) + if err != nil { + return err + } + + enc, _ := json.MarshalIndent(following, "", "\t") + w.Header().Set("Content-Type", config.ActivityStreams) + _, err = w.Write(enc) + + return err +} + +func GetActorFollowingDB(id string) ([]ObjectBase, error) { + var followingCollection []ObjectBase + query := `select following from following where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return followingCollection, err + } + defer rows.Close() + + for rows.Next() { + var obj ObjectBase + + if err := rows.Scan(&obj.Id); err != nil { + return followingCollection, err + } + + followingCollection = append(followingCollection, obj) + } + + return followingCollection, nil +} + +func GetActorFromDB(id string) (Actor, error) { + var nActor Actor + + query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return nActor, err + } + + var publicKeyPem string + defer rows.Close() + for rows.Next() { + if err := rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem); err != nil { + return nActor, err + } + } + + nActor.PublicKey, err = GetActorPemFromDB(publicKeyPem) + if err != nil { + return nActor, err + } + + if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { + if err := CreatePublicKeyFromPrivate(&nActor, publicKeyPem); err != nil { + return nActor, err + } + } + + return nActor, nil +} + +func GetActorFromJson(actor []byte) (Actor, error) { + var generic interface{} + var nActor Actor + err := json.Unmarshal(actor, &generic) + if err != nil { + return nActor, err + } + + if generic != nil { + switch generic.(type) { + case map[string]interface{}: + err = json.Unmarshal(actor, &nActor) + break + + case string: + var str string + err = json.Unmarshal(actor, &str) + nActor.Id = str + break + } + + return nActor, err + } + + return nActor, nil +} + +func GetActorInfo(ctx *fiber.Ctx, id string) error { + actor, err := GetActorFromDB(id) + if err != nil { + return err + } + + enc, _ := json.MarshalIndent(actor, "", "\t") + ctx.Response().Header.Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") + + _, err = ctx.Write(enc) + + return err +} + +func GetActorInstance(path string) (string, string) { + re := regexp.MustCompile(`([@]?([\w\d.-_]+)[@](.+))`) + atFormat := re.MatchString(path) + + if atFormat { + match := re.FindStringSubmatch(path) + if len(match) > 2 { + return match[2], match[3] + } + } + + re = regexp.MustCompile(`(https?://)?(www)?([\w\d-_.:]+)(/|\s+|\r|\r\n)?$`) + mainActor := re.MatchString(path) + if mainActor { + match := re.FindStringSubmatch(path) + if len(match) > 2 { + return "main", match[3] + } + } + + re = regexp.MustCompile(`(https?://)?(www)?([\w\d-_.:]+)\/([\w\d-_.]+)(\/([\w\d-_.]+))?`) + httpFormat := re.MatchString(path) + + if httpFormat { + match := re.FindStringSubmatch(path) + if len(match) > 3 { + if match[4] == "users" { + return match[6], match[3] + } + + return match[4], match[3] + } + } + + return "", "" +} + +func GetActorObjectCollectionFromDB(actorId string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' order by updated desc` + + rows, err := config.DB.Query(query, actorId) + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetActorReportedDB(id string) ([]ObjectBase, error) { + var nObj []ObjectBase + + query := `select id, count, reason from reported where board=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return nObj, err + } + + defer rows.Close() + + for rows.Next() { + var obj ObjectBase + + rows.Scan(&obj.Id, &obj.Size, &obj.Content) + + nObj = append(nObj, obj) + } + + return nObj, nil +} + +func GetActorReportedTotal(id string) (int, error) { + query := `select count(id) from reported where board=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return 0, err + } + + defer rows.Close() + + var count int + for rows.Next() { + rows.Scan(&count) + } + + return count, nil +} + +func GetActorsFollowPostFromId(actors []string, id string) (Collection, error) { + var collection Collection + + for _, e := range actors { + tempCol, err := GetObjectByIDFromDB(e + "/" + id) + if err != nil { + return collection, err + } + + if len(tempCol.OrderedItems) > 0 { + collection = tempCol + return collection, nil + } + } + + return collection, nil +} + +func GetAllActorArchiveDB(id string, offset int) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.updated from (select id, updated from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, updated from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, updated from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc offset $2` + + rows, err := config.DB.Query(query, id, offset) + if err != nil { + return nColl, err + } + defer rows.Close() + + for rows.Next() { + var post ObjectBase + + if err := rows.Scan(&post.Id, &post.Updated); err != nil { + return nColl, err + } + + post.Replies, _, _, err = GetObjectRepliesDB(post) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetBoards() ([]Actor, error) { + var boards []Actor + + query := `select type, id, name, preferedusername, inbox, outbox, following, followers FROM actor` + + rows, err := config.DB.Query(query) + if err != nil { + return boards, err + } + + defer rows.Close() + for rows.Next() { + var actor = new(Actor) + + if err := rows.Scan(&actor.Type, &actor.Id, &actor.Name, &actor.PreferredUsername, &actor.Inbox, &actor.Outbox, &actor.Following, &actor.Followers); err != nil { + return boards, err + } + + boards = append(boards, *actor) + } + + return boards, nil +} + +func IsActorLocal(id string) (bool, error) { + actor, err := GetActorFromDB(id) + return actor.Id != "", err +} + +func IsAlreadyFollowing(actor string, follow string) (bool, error) { + followers, err := GetActorFollowingDB(actor) + if err != nil { + return false, err + } + + for _, e := range followers { + if e.Id == follow { + return true, nil + } + } + + return false, nil +} + +func IsAlreadyFollower(actor string, follow string) (bool, error) { + followers, err := GetActorFollowDB(actor) + if err != nil { + return false, err + } + + for _, e := range followers { + if e.Id == follow { + return true, nil + } + } + + return false, nil +} + +func SetActorAutoSubscribeDB(id string) error { + current, err := GetActorAutoSubscribeDB(id) + if err != nil { + return err + } + + query := `update actor set autosubscribe=$1 where id=$2` + + _, err = config.DB.Exec(query, !current, id) + return err +} + +func SetActorFollowerDB(activity Activity) (Activity, error) { + var query string + alreadyFollow, err := IsAlreadyFollower(activity.Actor.Id, activity.Object.Actor) + if err != nil { + return activity, err + } + + activity.Type = "Reject" + if activity.Actor.Id == activity.Object.Actor { + return activity, nil + } + + if alreadyFollow { + query = `delete from follower where id=$1 and follower=$2` + activity.Summary = activity.Object.Actor + " Unfollow " + activity.Actor.Id + + if _, err := config.DB.Exec(query, activity.Actor.Id, activity.Object.Actor); err != nil { + return activity, err + } + + activity.Type = "Accept" + return activity, err + } + + query = `insert into follower (id, follower) values ($1, $2)` + activity.Summary = activity.Object.Actor + " Follow " + activity.Actor.Id + + if _, err := config.DB.Exec(query, activity.Actor.Id, activity.Object.Actor); err != nil { + return activity, err + } + + activity.Type = "Accept" + return activity, nil +} + +func WriteActorObjectReplyToDB(obj ObjectBase) error { + for _, e := range obj.InReplyTo { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := config.DB.Query(query, obj.Id, e.Id) + if err != nil { + return err + } + + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + if _, err := config.DB.Exec(query, obj.Id, e.Id); err != nil { + return err + } + } + } + + if len(obj.InReplyTo) < 1 { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := config.DB.Query(query, obj.Id, "") + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + if _, err := config.DB.Exec(query, obj.Id, ""); err != nil { + return err + } + } + } + + return nil +} + +func WriteActorObjectToCache(obj ObjectBase) (ObjectBase, error) { + if res, err := post.IsPostBlacklist(obj.Content); err == nil && res { + fmt.Println("\n\nBlacklist post blocked\n\n") + return obj, nil + } else if err != nil { + return obj, err + } + + if len(obj.Attachment) > 0 { + if res, err := IsIDLocal(obj.Id); err == nil && res { + return obj, err + } else if err != nil { + return obj, err + } + + if obj.Preview.Href != "" { + WritePreviewToCache(*obj.Preview) + } + + for i, _ := range obj.Attachment { + WriteAttachmentToCache(obj.Attachment[i]) + WriteActivitytoCacheWithAttachment(obj, obj.Attachment[i], *obj.Preview) + } + + } else { + WriteActivitytoCache(obj) + } + + WriteActorObjectReplyToDB(obj) + + if obj.Replies != nil { + for _, e := range obj.Replies.OrderedItems { + WriteActorObjectToCache(e) + } + } + + return obj, nil +} diff --git a/activitypub/object.go b/activitypub/object.go new file mode 100644 index 0000000..692fa5b --- /dev/null +++ b/activitypub/object.go @@ -0,0 +1,1802 @@ +package activitypub + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "os/exec" + "regexp" + "sort" + "strings" + "time" + + "github.com/FChannel0/FChannel-Server/config" + "github.com/FChannel0/FChannel-Server/post" + "github.com/FChannel0/FChannel-Server/util" +) + +func CheckIfObjectOP(id string) (bool, error) { + var count int + + query := `select count(id) from replies where inreplyto='' and id=$1 ` + + rows, err := config.DB.Query(query, id) + if err != nil { + return false, err + } + defer rows.Close() + + rows.Next() + if err := rows.Scan(&count); err != nil { + return false, err + } + + if count > 0 { + return true, nil + } + + return false, nil +} + +func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]ObjectBase, *os.File, error) { + contentType, err := post.GetFileContentType(file) + if err != nil { + return nil, nil, err + } + + filename := header.Filename + size := header.Size + + re := regexp.MustCompile(`.+/`) + + fileType := re.ReplaceAllString(contentType, "") + + tempFile, err := ioutil.TempFile("./public", "*."+fileType) + if err != nil { + return nil, nil, err + } + + var nAttachment []ObjectBase + var image ObjectBase + + image.Type = "Attachment" + image.Name = filename + image.Href = config.Domain + "/" + tempFile.Name() + image.MediaType = contentType + image.Size = size + image.Published = time.Now().UTC() + + nAttachment = append(nAttachment, image) + + return nAttachment, tempFile, nil +} + +func CreateObject(objType string) ObjectBase { + var nObj ObjectBase + + nObj.Type = objType + nObj.Published = time.Now().UTC() + nObj.Updated = time.Now().UTC() + + return nObj +} + +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 := util.GetUniqueFilename(file) + + nPreview.Type = "Preview" + nPreview.Name = obj.Name + nPreview.Href = config.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>", "-strip", "."+href) + + if err := cmd.Run(); err != nil { + // TODO: previously we would call CheckError here + var preview NestedObjectBase + return &preview + } + + return &nPreview +} + +func DeleteAttachmentFromDB(id string) error { + query := `delete from activitystream where id in (select attachment from activitystream where id=$1)` + + if _, err := config.DB.Exec(query, id); err != nil { + return err + } + + query = `delete from cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` + + _, err := config.DB.Exec(query, id) + return err +} + +func DeleteAttachmentFromFile(id string) error { + query := `select href from activitystream where id in (select attachment from activitystream where id=$1)` + + rows, err := config.DB.Query(query, id) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var href string + + if err := rows.Scan(&href); err != nil { + return err + } + + href = strings.Replace(href, config.Domain+"/", "", 1) + + if href != "static/notfound.png" { + _, err = os.Stat(href) + if err == nil { + os.Remove(href) + } + return err + } + } + + return nil +} + +func DeletePreviewFromDB(id string) error { + query := `delete from activitystream where id=$1` + + if _, err := config.DB.Exec(query, id); err != nil { + return err + } + + query = `delete from cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` + + _, err := config.DB.Exec(query, id) + return err +} + +func DeletePreviewFromFile(id string) error { + query := `select href from activitystream where id in (select preview from activitystream where id=$1)` + + rows, err := config.DB.Query(query, id) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var href string + + if err := rows.Scan(&href); err != nil { + return err + } + + href = strings.Replace(href, config.Domain+"/", "", 1) + + if href != "static/notfound.png" { + _, err = os.Stat(href) + if err == nil { + return os.Remove(href) + } + return err + } + } + + return nil +} + +func DeleteObject(id string) error { + if err := DeleteReportActivity(id); err != nil { + return err + } + + if err := DeleteAttachmentFromFile(id); err != nil { + return err + } + + if err := DeleteAttachmentFromDB(id); err != nil { + return err + } + + if err := DeletePreviewFromFile(id); err != nil { + return err + } + + if err := DeletePreviewFromDB(id); err != nil { + return err + } + + if err := DeleteObjectFromDB(id); err != nil { + return err + } + + return DeleteObjectRepliedTo(id) +} + +func DeleteObjectFromDB(id string) error { + var query = `delete from activitystream where id=$1` + + if _, err := config.DB.Exec(query, id); err != nil { + return err + } + + query = `delete from cacheactivitystream where id=$1` + + _, err := config.DB.Exec(query, id) + return err +} + +func DeleteObjectRepliedTo(id string) error { + query := `delete from replies where id=$1` + _, err := config.DB.Exec(query, id) + return err +} + +func DeleteObjectsInReplyTo(id string) error { + query := `delete from replies where id in (select id from replies where inreplyto=$1)` + _, err := config.DB.Exec(query, id) + return err +} + +func GetCollectionFromID(id string) (Collection, error) { + var nColl Collection + + req, err := http.NewRequest("GET", id, nil) + if err != nil { + return nColl, err + } + + req.Header.Set("Accept", config.ActivityStreams) + + resp, err := util.RouteProxy(req) + if err != nil { + return nColl, err + } + + if resp.StatusCode == 200 { + defer resp.Body.Close() + + body, _ := ioutil.ReadAll(resp.Body) + + if len(body) > 0 { + if err := json.Unmarshal(body, &nColl); err != nil { + return nColl, err + } + } + } + + return nColl, nil +} + +func GetCollectionFromPath(path string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc` + + rows, err := config.DB.Query(query, path) + if err != nil { + return nColl, err + } + defer rows.Close() + + for rows.Next() { + var actor Actor + var post ObjectBase + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + post.InReplyTo, err = GetInReplyToDB(post) + if err != nil { + return nColl, err + } + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = post.Replies.TotalItems + postCnt + post.Replies.TotalImgs = post.Replies.TotalImgs + imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.AtContext.Context = "https://www.w3.org/ns/activitystreams" + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetInReplyToDB(parent ObjectBase) ([]ObjectBase, error) { + var result []ObjectBase + + query := `select inreplyto from replies where id =$1` + + rows, err := config.DB.Query(query, parent.Id) + if err != nil { + return result, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + + if err := rows.Scan(&post.Id); err != nil { + return result, err + } + + result = append(result, post) + } + + return result, nil +} + +func GetObjectAttachment(id string) ([]ObjectBase, error) { + var attachments []ObjectBase + + query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` + + rows, err := config.DB.Query(query, id) + if err != nil { + return attachments, err + } + + defer rows.Close() + for rows.Next() { + var attachment = new(ObjectBase) + + if err := rows.Scan(&attachment.Id, &attachment.Type, &attachment.Name, &attachment.Href, &attachment.MediaType, &attachment.Size, &attachment.Published); err != nil { + return attachments, err + } + + attachments = append(attachments, *attachment) + } + + return attachments, nil +} + +func GetObjectByIDFromDB(postID string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and (type='Note' or type='Archive') union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and (type='Note' or type='Archive')) as x` + + rows, err := config.DB.Query(query, postID) + if err != nil { + return nColl, err + } + defer rows.Close() + + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + actor, err = GetActorFromDB(actor.Id) + if err != nil { + return nColl, err + } + + post.Actor = actor.Id + + nColl.Actor = &actor + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetObjectFromDB(id string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 order by updated desc` + + rows, err := config.DB.Query(query, id) + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetObjectFromDBCatalog(id string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 165` + + rows, err := config.DB.Query(query, id) + + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var replies CollectionBase + + post.Replies = &replies + + post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) + + if err != nil { + return nColl, err + } + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetObjectFromDBFromID(id string) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id like $1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id like $1 and type='Note') as x order by x.updated` + + re := regexp.MustCompile(`f(\w+)\-`) + match := re.FindStringSubmatch(id) + + if len(match) > 0 { + re := regexp.MustCompile(`(.+)\-`) + id = re.ReplaceAllString(id, "") + id = "%" + match[1] + "/" + id + } + + rows, err := config.DB.Query(query, id) + if err != nil { + return nColl, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl, nil +} + +func GetObjectFromDBPage(id string, page int) (Collection, error) { + var nColl Collection + var result []ObjectBase + + query := `select count (x.id) over(), x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 15 offset $2` + + rows, err := config.DB.Query(query, id, page*15) + if err != nil { + return nColl, err + } + + var count int + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + if err := rows.Scan(&count, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return nColl, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + var err error + post.Replies, postCnt, imgCnt, err = GetObjectRepliesDBLimit(post, 5) + if err != nil { + return nColl, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nColl, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return nColl, err + } + + result = append(result, post) + } + + nColl.TotalItems = count + nColl.OrderedItems = result + + return nColl, nil +} + +func GetObjectFromJson(obj []byte) (ObjectBase, error) { + var generic interface{} + var nObj ObjectBase + + if err := json.Unmarshal(obj, &generic); err != nil { + return ObjectBase{}, err + } + + if generic != nil { + switch generic.(type) { + case []interface{}: + var lObj ObjectBase + var arrContext ObjectArray + + if err := json.Unmarshal(obj, &arrContext.Object); err != nil { + return nObj, err + } + + if len(arrContext.Object) > 0 { + lObj = arrContext.Object[0] + } + nObj = lObj + break + + case map[string]interface{}: + var arrContext Object + + if err := json.Unmarshal(obj, &arrContext.Object); err != nil { + return nObj, err + } + + nObj = *arrContext.Object + break + + case string: + var lObj ObjectBase + var arrContext ObjectString + + if err := json.Unmarshal(obj, &arrContext.Object); err != nil { + return nObj, err + } + + lObj.Id = arrContext.Object + nObj = lObj + break + } + } + + return nObj, nil +} + +func GetObjectFromPath(path string) (ObjectBase, error) { + var nObj ObjectBase + + query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc` + + rows, err := config.DB.Query(query, path) + if err != nil { + return nObj, err + } + + defer rows.Close() + rows.Next() + var attachID string + var previewID string + + var nActor Actor + nObj.Actor = nActor.Id + + if err := rows.Scan(&nObj.Id, &nObj.Name, &nObj.Content, &nObj.Type, &nObj.Published, &nObj.AttributedTo, &attachID, &previewID, &nObj.Actor); err != nil { + return nObj, err + } + + var postCnt int + var imgCnt int + + nObj.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(nObj) + if err != nil { + return nObj, err + } + + nObj.Replies.TotalItems, nObj.Replies.TotalImgs, err = GetObjectRepliesCount(nObj) + if err != nil { + return nObj, err + } + + nObj.Replies.TotalItems = nObj.Replies.TotalItems + postCnt + nObj.Replies.TotalImgs = nObj.Replies.TotalImgs + imgCnt + + nObj.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return nObj, err + } + + nObj.Preview, err = GetObjectPreview(previewID) + return nObj, err +} + +func GetObjectImgsTotalDB(actor Actor) (int, error) { + count := 0 + query := `select count(attachment) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note' )` + + rows, err := config.DB.Query(query, actor.Id) + if err != nil { + return count, err + } + defer rows.Close() + + for rows.Next() { + if err := rows.Scan(&count); err != nil { + return count, err + } + } + + return count, nil +} + +func GetObjectPostsTotalDB(actor Actor) (int, error) { + count := 0 + query := `select count(id) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note')` + + rows, err := config.DB.Query(query, actor.Id) + if err != nil { + return count, err + } + + defer rows.Close() + for rows.Next() { + if err := rows.Scan(&count); err != nil { + return count, err + } + } + + return count, nil +} + +func GetObjectPreview(id string) (*NestedObjectBase, error) { + var preview NestedObjectBase + + query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` + + rows, err := config.DB.Query(query, id) + if err != nil { + return nil, err + } + + defer rows.Close() + for rows.Next() { + if err := rows.Scan(&preview.Id, &preview.Type, &preview.Name, &preview.Href, &preview.MediaType, &preview.Size, &preview.Published); err != nil { + return nil, err + } + } + + return &preview, nil +} + +func GetObjectRepliesCount(parent ObjectBase) (int, int, error) { + var countId int + var countImg int + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over() from (select id, attachment from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select id, attachment from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x` + + rows, err := config.DB.Query(query, parent.Id) + if err != nil { + return 0, 0, err + } + + defer rows.Close() + + for rows.Next() { + err = rows.Scan(&countId, &countImg) + } + + return countId, countImg, err +} + +func GetObjectRepliesDB(parent ObjectBase) (*CollectionBase, int, int, error) { + var nColl CollectionBase + var result []ObjectBase + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive')) as x order by x.published asc` + + rows, err := config.DB.Query(query, parent.Id) + if err != nil { + return nil, 0, 0, err + } + + var postCount int + var attachCount int + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + if err := rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return &nColl, postCount, attachCount, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + + post.Replies, postCnt, imgCnt, err = GetObjectRepliesRepliesDB(post) + if err != nil { + return &nColl, postCount, attachCount, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return &nColl, postCount, attachCount, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return &nColl, postCount, attachCount, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return &nColl, postCount, attachCount, nil +} + +func GetObjectRepliesDBLimit(parent ObjectBase, limit int) (*CollectionBase, int, int, error) { + var nColl CollectionBase + var result []ObjectBase + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published desc limit $2` + + rows, err := config.DB.Query(query, parent.Id, limit) + if err != nil { + return nil, 0, 0, err + } + + var postCount int + var attachCount int + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + if err := rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return &nColl, postCount, attachCount, err + } + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + + post.Replies, postCnt, imgCnt, err = GetObjectRepliesRepliesDB(post) + if err != nil { + return &nColl, postCount, attachCount, err + } + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return &nColl, postCount, attachCount, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return &nColl, postCount, attachCount, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + sort.Sort(ObjectBaseSortAsc(nColl.OrderedItems)) + + return &nColl, postCount, attachCount, nil +} + +func GetObjectRepliesReplies(parent ObjectBase) (*CollectionBase, int, int, error) { + var nColl CollectionBase + var result []ObjectBase + + query := `select id, name, content, type, published, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') order by updated asc` + + rows, err := config.DB.Query(query, parent.Id) + if err != nil { + return &nColl, 0, 0, err + } + + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return &nColl, 0, 0, err + } + + post.Actor = actor.Id + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return &nColl, 0, 0, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return &nColl, 0, 0, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return &nColl, 0, 0, nil +} + +func GetObjectRepliesRepliesDB(parent ObjectBase) (*CollectionBase, int, int, error) { + var nColl CollectionBase + var result []ObjectBase + + query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published asc` + + rows, err := config.DB.Query(query, parent.Id) + if err != nil { + return &nColl, 0, 0, err + } + + var postCount int + var attachCount int + defer rows.Close() + for rows.Next() { + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + post.InReplyTo = append(post.InReplyTo, parent) + + if err := rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { + return &nColl, postCount, attachCount, err + } + + post.Actor = actor.Id + + post.Attachment, err = GetObjectAttachment(attachID) + if err != nil { + return &nColl, postCount, attachCount, err + } + + post.Preview, err = GetObjectPreview(previewID) + if err != nil { + return &nColl, postCount, attachCount, err + } + + result = append(result, post) + } + + nColl.OrderedItems = result + + return &nColl, postCount, attachCount, nil +} + +func GetObjectTypeDB(id string) (string, error) { + query := `select type from activitystream where id=$1 union select type from cacheactivitystream where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return "", err + } + defer rows.Close() + + var nType string + rows.Next() + rows.Scan(&nType) + + return nType, nil +} + +func GetObjectsWithoutPreviewsCallback(callback func(id string, href string, mediatype string, name string, size int, published time.Time) error) error { + query := `select id, href, mediatype, name, size, published from activitystream where id in (select attachment from activitystream where attachment!='' and preview='')` + + rows, err := config.DB.Query(query) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var id string + var href string + var mediatype string + var name string + var size int + var published time.Time + + if err := rows.Scan(&id, &href, &mediatype, &name, &size, &published); err != nil { + return err + } + + if err := callback(id, href, mediatype, name, size, published); err != nil { + return err + } + } + + return nil +} + +func GetToFromJson(to []byte) ([]string, error) { + var generic interface{} + + err := json.Unmarshal(to, &generic) + if err != nil { + return nil, err + } + + if generic != nil { + var nStr []string + switch generic.(type) { + case []interface{}: + err = json.Unmarshal(to, &nStr) + break + case string: + var str string + err = json.Unmarshal(to, &str) + nStr = append(nStr, str) + break + } + return nStr, err + } + + return nil, nil +} + +func IsObjectCached(id string) (bool, error) { + query := `select id from cacheactivitystream where id=$1` + rows, err := config.DB.Query(query, id) + if err != nil { + return false, err + } + + var nID string + defer rows.Close() + + rows.Next() + err = rows.Scan(&nID) + return nID != "", err +} + +func IsIDLocal(id string) (bool, error) { + activity, err := GetActivityFromDB(id) + return len(activity.OrderedItems) > 0, err +} + +func IsObjectLocal(id string) (bool, error) { + query := `select id from activitystream where id=$1` + + rows, err := config.DB.Query(query, id) + if err != nil { + return false, err + } + + var nID string + defer rows.Close() + + rows.Next() + err = rows.Scan(&nID) + return nID != "", err +} + +func MarkObjectSensitive(id string, sensitive bool) error { + var query = `update activitystream set sensitive=$1 where id=$2` + if _, err := config.DB.Exec(query, sensitive, id); err != nil { + return err + } + + query = `update cacheactivitystream set sensitive=$1 where id=$2` + _, err := config.DB.Exec(query, sensitive, id) + return err +} + +func ObjectFromJson(r *http.Request, obj ObjectBase) (ObjectBase, error) { + body, _ := ioutil.ReadAll(r.Body) + + var respActivity ActivityRaw + + err := json.Unmarshal(body, &respActivity) + if err != nil { + return obj, err + } + + res, err := HasContextFromJson(respActivity.AtContextRaw.Context) + + if err == nil && res { + var jObj ObjectBase + jObj, err = GetObjectFromJson(respActivity.ObjectRaw) + if err != nil { + return obj, err + } + + jObj.To, err = GetToFromJson(respActivity.ToRaw) + if err != nil { + return obj, err + } + + jObj.Cc, err = GetToFromJson(respActivity.CcRaw) + } + + return obj, err +} + +func SetAttachmentFromDB(id string, _type string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id=$3)` + + if _, err := config.DB.Exec(query, _type, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` + + _, err := config.DB.Exec(query, _type, datetime, id) + return err +} + +func SetAttachmentRepliesFromDB(id string, _type string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id in (select id from replies where inreplyto=$3))` + + if _, err := config.DB.Exec(query, _type, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id in (select id from replies where inreplyto=$3))` + + _, err := config.DB.Exec(query, _type, datetime, id) + return err +} + +func SetPreviewFromDB(id string, _type string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id=$3)` + + if _, err := config.DB.Exec(query, _type, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` + + _, err := config.DB.Exec(query, _type, datetime, id) + return err +} + +func SetPreviewRepliesFromDB(id string, _type string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id in (select id from replies where inreplyto=$3))` + + if _, err := config.DB.Exec(query, _type, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id in (select id from replies where inreplyto=$3))` + + _, err := config.DB.Exec(query, _type, datetime, id) + return err +} + +func SetObject(id string, _type string) error { + if err := SetAttachmentFromDB(id, _type); err != nil { + return err + } + + if err := SetPreviewFromDB(id, _type); err != nil { + return err + } + + return SetObjectFromDB(id, _type) +} + +func SetObjectAndReplies(id string, _type string) error { + if err := SetAttachmentFromDB(id, _type); err != nil { + return err + } + + if err := SetPreviewFromDB(id, _type); err != nil { + return err + } + + if err := SetObjectRepliesFromDB(id, _type); err != nil { + return err + } + + if err := SetAttachmentRepliesFromDB(id, _type); err != nil { + return err + } + + if err := SetPreviewRepliesFromDB(id, _type); err != nil { + return err + } + + return SetObjectFromDB(id, _type) +} + +func SetObjectFromDB(id string, _type string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type=$1, deleted=$2 where id=$3` + + if _, err := config.DB.Exec(query, _type, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type=$1, deleted=$2 where id=$3` + + _, err := config.DB.Exec(query, _type, datetime, id) + return err +} + +func SetObjectRepliesFromDB(id string, _type string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + var query = `update activitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` + if _, err := config.DB.Exec(query, _type, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` + _, err := config.DB.Exec(query, _type, datetime, id) + return err +} + +func SetObjectType(id string, nType string) error { + col, err := GetObjectFromDB(id) + if err != nil { + return err + } + + for _, e := range col.OrderedItems { + for _, k := range e.Replies.OrderedItems { + if err := UpdateObjectTypeDB(k.Id, nType); err != nil { + return err + } + } + + if err := UpdateObjectTypeDB(e.Id, nType); err != nil { + return err + } + } + + return nil +} + +func TombstoneAttachmentFromDB(id string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from activitystream where id=$3)` + + if _, err := config.DB.Exec(query, config.Domain+"/static/notfound.png", datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` + + _, err := config.DB.Exec(query, config.Domain+"/static/notfound.png", datetime, id) + return err +} + +func TombstoneAttachmentRepliesFromDB(id string) error { + query := `select id from activitystream where id in (select id from replies where inreplyto=$1)` + + rows, err := config.DB.Query(query, id) + if err != nil { + return err + } + + defer rows.Close() + for rows.Next() { + var attachment string + + if err := rows.Scan(&attachment); err != nil { + return err + } + + if err := DeleteAttachmentFromFile(attachment); err != nil { + return err + } + + if err := TombstoneAttachmentFromDB(attachment); err != nil { + return err + } + } + + return nil +} + +func TombstonePreviewFromDB(id string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from activitystream where id=$3)` + + if _, err := config.DB.Exec(query, config.Domain+"/static/notfound.png", datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` + + _, err := config.DB.Exec(query, config.Domain+"/static/notfound.png", datetime, id) + return err +} + +func TombstonePreviewRepliesFromDB(id string) error { + query := `select id from activitystream where id in (select id from replies where inreplyto=$1)` + + rows, err := config.DB.Query(query, id) + if err != nil { + return err + } + + defer rows.Close() + for rows.Next() { + var attachment string + + if err := rows.Scan(&attachment); err != nil { + return err + } + + if err := DeletePreviewFromFile(attachment); err != nil { + return err + } + + if err := TombstonePreviewFromDB(attachment); err != nil { + return err + } + } + + return nil +} + +func TombstoneObject(id string) error { + if err := DeleteReportActivity(id); err != nil { + return err + } + + if err := DeleteAttachmentFromFile(id); err != nil { + return err + } + + if err := TombstoneAttachmentFromDB(id); err != nil { + return err + } + + if err := DeletePreviewFromFile(id); err != nil { + return err + } + + if err := TombstonePreviewFromDB(id); err != nil { + return err + } + + return TombstoneObjectFromDB(id) +} + +func TombstoneObjectAndReplies(id string) error { + if err := DeleteReportActivity(id); err != nil { + return err + } + + if err := DeleteAttachmentFromFile(id); err != nil { + return err + } + + if err := TombstoneAttachmentFromDB(id); err != nil { + return err + } + + if err := DeletePreviewFromFile(id); err != nil { + return err + } + + if err := TombstonePreviewFromDB(id); err != nil { + return err + } + + if err := TombstoneObjectRepliesFromDB(id); err != nil { + return err + } + + if err := TombstoneAttachmentRepliesFromDB(id); err != nil { + return err + } + + if err := TombstonePreviewRepliesFromDB(id); err != nil { + return err + } + + return TombstoneObjectFromDB(id) +} + +func TombstoneObjectFromDB(id string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + query := `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` + + if _, err := config.DB.Exec(query, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` + + _, err := config.DB.Exec(query, datetime, id) + return err +} + +func TombstoneObjectRepliesFromDB(id string) error { + datetime := time.Now().UTC().Format(time.RFC3339) + + query := `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` + + if _, err := config.DB.Exec(query, datetime, id); err != nil { + return err + } + + query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` + + _, err := config.DB.Exec(query, datetime, id) + return err +} + +func UpdateObjectTypeDB(id string, nType string) error { + query := `update activitystream set type=$2 where id=$1 and type !='Tombstone'` + if _, err := config.DB.Exec(query, id, nType); err != nil { + return err + } + + query = `update cacheactivitystream set type=$2 where id=$1 and type !='Tombstone'` + _, err := config.DB.Exec(query, id, nType) + return err +} + +func UpdateObjectWithPreview(id string, preview string) error { + query := `update activitystream set preview=$1 where attachment=$2` + + _, err := config.DB.Exec(query, preview, id) + return err +} + +func AddFollower(id string, follower string) error { + query := `insert into follower (id, follower) values ($1, $2)` + + _, err := config.DB.Exec(query, id, follower) + return err +} + +func WriteObjectReplyToDB(obj ObjectBase) error { + for i, e := range obj.InReplyTo { + + if res, err := CheckIfObjectOP(obj.Id); err == nil && !res && i == 0 { + nType, err := GetObjectTypeDB(e.Id) + if err != nil { + return err + } + + if nType == "Archive" { + if err := UpdateObjectTypeDB(obj.Id, "Archive"); err != nil { + return err + } + } + } else if err != nil { + return err + } + + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := config.DB.Query(query, obj.Id, e.Id) + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + if err := rows.Scan(&id); err != nil { + return err + } + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + _, err := config.DB.Exec(query, obj.Id, e.Id) + if err != nil { + return err + } + } + + update := true + for _, e := range obj.Option { + if e == "sage" || e == "nokosage" { + update = false + break + } + } + + if update { + if err := WriteObjectUpdatesToDB(e); err != nil { + return err + } + } + } + + if len(obj.InReplyTo) < 1 { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := config.DB.Query(query, obj.Id, "") + if err != nil { + return err + } + defer rows.Close() + + var id string + rows.Next() + rows.Scan(&id) + + if id == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + if _, err := config.DB.Exec(query, obj.Id, ""); err != nil { + return err + } + } + } + + return nil +} + +func WriteObjectReplyToLocalDB(id string, replyto string) error { + query := `select id from replies where id=$1 and inreplyto=$2` + + rows, err := config.DB.Query(query, id, replyto) + if err != nil { + return err + } + defer rows.Close() + + var nID string + rows.Next() + rows.Scan(&nID) + + if nID == "" { + query := `insert into replies (id, inreplyto) values ($1, $2)` + + if _, err := config.DB.Exec(query, id, replyto); err != nil { + return err + } + } + + query = `select inreplyto from replies where id=$1` + + rows, err = config.DB.Query(query, replyto) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var val string + rows.Scan(&val) + if val == "" { + updated := time.Now().UTC().Format(time.RFC3339) + query := `update activitystream set updated=$1 where id=$2` + + if _, err := config.DB.Exec(query, updated, replyto); err != nil { + return err + } + } + } + + return nil +} + +func WriteObjectToCache(obj ObjectBase) (ObjectBase, error) { + if res, err := post.IsPostBlacklist(obj.Content); err == nil && res { + fmt.Println("\n\nBlacklist post blocked\n\n") + return obj, nil + } else { + return obj, err + } + + if len(obj.Attachment) > 0 { + if obj.Preview.Href != "" { + WritePreviewToCache(*obj.Preview) + } + + for i, _ := range obj.Attachment { + WriteAttachmentToCache(obj.Attachment[i]) + WriteActivitytoCacheWithAttachment(obj, obj.Attachment[i], *obj.Preview) + } + + } else { + WriteActivitytoCache(obj) + } + + WriteObjectReplyToDB(obj) + + if obj.Replies != nil { + for _, e := range obj.Replies.OrderedItems { + WriteObjectToCache(e) + } + } + + return obj, nil +} + +func WriteObjectToDB(obj ObjectBase) (ObjectBase, error) { + id, err := util.CreateUniqueID(obj.Actor) + if err != nil { + return obj, err + } + + obj.Id = fmt.Sprintf("%s/%s", obj.Actor, id) + if len(obj.Attachment) > 0 { + if obj.Preview.Href != "" { + id, err := util.CreateUniqueID(obj.Actor) + if err != nil { + return obj, err + } + + obj.Preview.Id = fmt.Sprintf("%s/%s", obj.Actor, id) + obj.Preview.Published = time.Now().UTC() + obj.Preview.Updated = time.Now().UTC() + obj.Preview.AttributedTo = obj.Id + if err := WritePreviewToDB(*obj.Preview); err != nil { + return obj, err + } + } + + for i := range obj.Attachment { + id, err := util.CreateUniqueID(obj.Actor) + if err != nil { + return obj, err + } + + obj.Attachment[i].Id = fmt.Sprintf("%s/%s", obj.Actor, id) + obj.Attachment[i].Published = time.Now().UTC() + obj.Attachment[i].Updated = time.Now().UTC() + obj.Attachment[i].AttributedTo = obj.Id + WriteAttachmentToDB(obj.Attachment[i]) + WriteActivitytoDBWithAttachment(obj, obj.Attachment[i], *obj.Preview) + } + + } else { + if err := WriteActivitytoDB(obj); err != nil { + return obj, err + } + } + + if err := WriteObjectReplyToDB(obj); err != nil { + return obj, err + } + + err = WriteWalletToDB(obj) + return obj, err +} + +func WriteObjectUpdatesToDB(obj ObjectBase) error { + query := `update activitystream set updated=$1 where id=$2` + + if _, err := config.DB.Exec(query, time.Now().UTC().Format(time.RFC3339), obj.Id); err != nil { + return err + } + + query = `update cacheactivitystream set updated=$1 where id=$2` + + _, err := config.DB.Exec(query, time.Now().UTC().Format(time.RFC3339), obj.Id) + return err +} + +func WriteWalletToDB(obj ObjectBase) error { + for _, e := range obj.Option { + if e == "wallet" { + for _, e := range obj.Wallet { + query := `insert into wallet (id, type, address) values ($1, $2, $3)` + + if _, err := config.DB.Exec(query, obj.Id, e.Type, e.Address); err != nil { + return err + } + } + + return nil + } + } + return nil +} diff --git a/activitypub/pem.go b/activitypub/pem.go new file mode 100644 index 0000000..ca6c068 --- /dev/null +++ b/activitypub/pem.go @@ -0,0 +1,241 @@ +package activitypub + +import ( + crand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" + + "github.com/FChannel0/FChannel-Server/config" +) + +type Signature struct { + KeyId string + Headers []string + Signature string + Algorithm string +} + +func CreatePem(actor Actor) error { + privatekey, err := rsa.GenerateKey(crand.Reader, 2048) + if err != nil { + return err + } + + privateKeyBytes := x509.MarshalPKCS1PrivateKey(privatekey) + + privateKeyBlock := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: privateKeyBytes, + } + + privatePem, err := os.Create("./pem/board/" + actor.Name + "-private.pem") + if err != nil { + return err + } + + if err := pem.Encode(privatePem, privateKeyBlock); err != nil { + return err + } + + publickey := &privatekey.PublicKey + publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey) + if err != nil { + return err + } + + publicKeyBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyBytes, + } + + publicPem, err := os.Create("./pem/board/" + actor.Name + "-public.pem") + if err != nil { + return err + } + + if err := pem.Encode(publicPem, publicKeyBlock); err != nil { + return err + } + + _, err = os.Stat("./pem/board/" + actor.Name + "-public.pem") + if os.IsNotExist(err) { + return err + } else { + return StorePemToDB(actor) + } + + fmt.Println(`Created PEM keypair for the "` + actor.Name + `" board. Please keep in mind that +the PEM key is crucial in identifying yourself as the legitimate owner of the board, +so DO NOT LOSE IT!!! If you lose it, YOU WILL LOSE ACCESS TO YOUR BOARD!`) + + return nil +} + +func CreatePublicKeyFromPrivate(actor *Actor, publicKeyPem string) error { + publicFilename, err := GetActorPemFileFromDB(publicKeyPem) + if err != nil { + return err + } + + privateFilename := strings.ReplaceAll(publicFilename, "public.pem", "private.pem") + if _, err := os.Stat(privateFilename); err == nil { + // Not a lost cause + priv, err := ioutil.ReadFile(privateFilename) + if err != nil { + return err + } + + block, _ := pem.Decode([]byte(priv)) + if block == nil || block.Type != "RSA PRIVATE KEY" { + return errors.New("failed to decode PEM block containing public key") + } + + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return err + } + + publicKeyDer, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + if err != nil { + return err + } + + pubKeyBlock := pem.Block{ + Type: "PUBLIC KEY", + Headers: nil, + Bytes: publicKeyDer, + } + + publicFileWriter, err := os.Create(publicFilename) + if err != nil { + return err + } + + if err := pem.Encode(publicFileWriter, &pubKeyBlock); err != nil { + return err + } + } else { + fmt.Println(`\nUnable to locate private key from public key generation. Now, +this means that you are now missing the proof that you are the +owner of the "` + actor.Name + `" board. If you are the developer, +then your job is just as easy as generating a new keypair, but +if this board is live, then you'll also have to convince the other +owners to switch their public keys for you so that they will start +accepting your posts from your board from this site. Good luck ;)`) + return errors.New("unable to locate private key") + } + return nil +} + +func GetActorPemFromDB(pemID string) (PublicKeyPem, error) { + var pem PublicKeyPem + + query := `select id, owner, file from publickeypem where id=$1` + + rows, err := config.DB.Query(query, pemID) + if err != nil { + return pem, err + } + + defer rows.Close() + + rows.Next() + rows.Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem) + f, err := os.ReadFile(pem.PublicKeyPem) + if err != nil { + return pem, err + } + + pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) + + return pem, nil +} + +func GetActorPemFileFromDB(pemID string) (string, error) { + query := `select file from publickeypem where id=$1` + rows, err := config.DB.Query(query, pemID) + if err != nil { + return "", err + } + + defer rows.Close() + + var file string + rows.Next() + rows.Scan(&file) + + return file, nil +} + +func StorePemToDB(actor Actor) error { + query := "select publicKeyPem from actor where id=$1" + rows, err := config.DB.Query(query, actor.Id) + if err != nil { + return err + } + + defer rows.Close() + + var result string + rows.Next() + rows.Scan(&result) + + if result != "" { + return errors.New("already storing public key for actor") + } + + publicKeyPem := actor.Id + "#main-key" + query = "update actor set publicKeyPem=$1 where id=$2" + if _, err := config.DB.Exec(query, publicKeyPem, actor.Id); err != nil { + return err + } + + file := "./pem/board/" + actor.Name + "-public.pem" + query = "insert into publicKeyPem (id, owner, file) values($1, $2, $3)" + _, err = config.DB.Exec(query, publicKeyPem, actor.Id, file) + return err +} + +func ParseHeaderSignature(signature string) Signature { + var nsig Signature + + keyId := regexp.MustCompile(`keyId=`) + headers := regexp.MustCompile(`headers=`) + sig := regexp.MustCompile(`signature=`) + algo := regexp.MustCompile(`algorithm=`) + + signature = strings.ReplaceAll(signature, "\"", "") + parts := strings.Split(signature, ",") + + for _, e := range parts { + if keyId.MatchString(e) { + nsig.KeyId = keyId.ReplaceAllString(e, "") + continue + } + + if headers.MatchString(e) { + header := headers.ReplaceAllString(e, "") + nsig.Headers = strings.Split(header, " ") + continue + } + + if sig.MatchString(e) { + nsig.Signature = sig.ReplaceAllString(e, "") + continue + } + + if algo.MatchString(e) { + nsig.Algorithm = algo.ReplaceAllString(e, "") + continue + } + } + + return nsig +} @@ -156,7 +156,7 @@ func ParseLinkComments(board activitypub.Actor, op string, content string, threa } if quoteTitle == "" { - obj, err := db.GetObjectFromDBFromID(parsedLink) + obj, err := activitypub.GetObjectFromDBFromID(parsedLink) if err != nil { return "", err } diff --git a/config/config.go b/config/config.go index 93531b8..10c029c 100644 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,7 @@ package config import ( "bufio" + "database/sql" "log" "os" "strconv" @@ -30,6 +31,7 @@ var ActivityStreams = "application/ld+json; profile=\"https://www.w3.org/ns/acti var SupportedFiles = []string{"image/gif", "image/jpeg", "image/png", "image/webp", "image/apng", "video/mp4", "video/ogg", "video/webm", "audio/mpeg", "audio/ogg", "audio/wav", "audio/wave", "audio/x-wav"} var Key string var Themes []string +var DB *sql.DB func GetConfigValue(value string, ifnone string) string { file, err := os.Open("config/config-init") diff --git a/db/actor.go b/db/actor.go deleted file mode 100644 index 7e36090..0000000 --- a/db/actor.go +++ /dev/null @@ -1,67 +0,0 @@ -package db - -import ( - "encoding/json" - "fmt" - "regexp" - "strings" - - "github.com/FChannel0/FChannel-Server/activitypub" - "github.com/gofiber/fiber/v2" -) - -func GetActorFromPath(location string, prefix string) (activitypub.Actor, error) { - 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 activitypub.Actor - - nActor, err := GetActorByNameFromDB(actor) - if err != nil { - return nActor, err - } - - if nActor.Id == "" { - nActor = GetActorByName(actor) - } - - return nActor, nil -} - -func GetActorByName(name string) activitypub.Actor { - var actor activitypub.Actor - for _, e := range Boards { - if e.Actor.Name == name { - actor = e.Actor - } - } - - return actor -} - -func GetActorInfo(ctx *fiber.Ctx, id string) error { - actor, err := GetActorFromDB(id) - if err != nil { - return err - } - - enc, _ := json.MarshalIndent(actor, "", "\t") - ctx.Response().Header.Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") - - _, err = ctx.Write(enc) - - return err -} diff --git a/db/cache.go b/db/cache.go index dff1987..71e244f 100644 --- a/db/cache.go +++ b/db/cache.go @@ -1,206 +1,12 @@ package db import ( - "fmt" - "github.com/FChannel0/FChannel-Server/activitypub" - "github.com/FChannel0/FChannel-Server/util" + "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/webfinger" _ "github.com/lib/pq" ) -func WriteObjectToCache(obj activitypub.ObjectBase) (activitypub.ObjectBase, error) { - if res, err := IsPostBlacklist(obj.Content); err == nil && res { - fmt.Println("\n\nBlacklist post blocked\n\n") - return obj, nil - } else { - return obj, err - } - - if len(obj.Attachment) > 0 { - if obj.Preview.Href != "" { - WritePreviewToCache(*obj.Preview) - } - - for i, _ := range obj.Attachment { - WriteAttachmentToCache(obj.Attachment[i]) - WriteActivitytoCacheWithAttachment(obj, obj.Attachment[i], *obj.Preview) - } - - } else { - WriteActivitytoCache(obj) - } - - WriteObjectReplyToDB(obj) - - if obj.Replies != nil { - for _, e := range obj.Replies.OrderedItems { - WriteObjectToCache(e) - } - } - - return obj, nil -} - -func WriteActorObjectToCache(obj activitypub.ObjectBase) (activitypub.ObjectBase, error) { - if res, err := IsPostBlacklist(obj.Content); err == nil && res { - fmt.Println("\n\nBlacklist post blocked\n\n") - return obj, nil - } else if err != nil { - return obj, err - } - - if len(obj.Attachment) > 0 { - if res, err := IsIDLocal(obj.Id); err == nil && res { - return obj, err - } else if err != nil { - return obj, err - } - - if obj.Preview.Href != "" { - WritePreviewToCache(*obj.Preview) - } - - for i, _ := range obj.Attachment { - WriteAttachmentToCache(obj.Attachment[i]) - WriteActivitytoCacheWithAttachment(obj, obj.Attachment[i], *obj.Preview) - } - - } else { - WriteActivitytoCache(obj) - } - - WriteActorObjectReplyToDB(obj) - - if obj.Replies != nil { - for _, e := range obj.Replies.OrderedItems { - WriteActorObjectToCache(e) - } - } - - return obj, nil -} - -func WriteActivitytoCache(obj activitypub.ObjectBase) error { - obj.Name = util.EscapeString(obj.Name) - obj.Content = util.EscapeString(obj.Content) - obj.AttributedTo = util.EscapeString(obj.AttributedTo) - - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - err = rows.Scan(&id) - if err != nil { - return err - } else if id != "" { - return nil // TODO: error? - } - - if obj.Updated.IsZero() { - obj.Updated = obj.Published - } - - query = `insert into cacheactivitystream (id, type, name, content, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` - - _, err = db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - return err -} - -func WriteActivitytoCacheWithAttachment(obj activitypub.ObjectBase, attachment activitypub.ObjectBase, preview activitypub.NestedObjectBase) error { - obj.Name = util.EscapeString(obj.Name) - obj.Content = util.EscapeString(obj.Content) - obj.AttributedTo = util.EscapeString(obj.AttributedTo) - - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - err = rows.Scan(&id) - if err != nil { - return err - } else if id != "" { - return nil // TODO: error? - } - - if obj.Updated.IsZero() { - obj.Updated = obj.Published - } - - query = `insert into cacheactivitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` - - _, err = db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - return err -} - -func WriteAttachmentToCache(obj activitypub.ObjectBase) error { - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - err = rows.Scan(&id) - if err != nil { - return err - } else if id != "" { - return nil // TODO: error? - } - - if obj.Updated.IsZero() { - obj.Updated = obj.Published - } - - query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, err = db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) - return err -} - -func WritePreviewToCache(obj activitypub.NestedObjectBase) error { - query := `select id from cacheactivitystream where id=$1` - - rows, err := db.Query(query, obj.Id) - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - err = rows.Scan(&id) - if err != nil { - return err - } else if id != "" { - return nil // TODO: error? - } - - if obj.Updated.IsZero() { - obj.Updated = obj.Published - } - - query = `insert into cacheactivitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, err = db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) - return err -} - func WriteObjectReplyToCache(obj activitypub.ObjectBase) error { for i, e := range obj.InReplyTo { res, err := IsReplyInThread(obj.InReplyTo[0].Id, e.Id) @@ -211,7 +17,7 @@ func WriteObjectReplyToCache(obj activitypub.ObjectBase) error { if i == 0 || res { query := `select id from replies where id=$1` - rows, err := db.Query(query, obj.Id) + rows, err := config.DB.Query(query, obj.Id) if err != nil { return err } @@ -228,7 +34,7 @@ func WriteObjectReplyToCache(obj activitypub.ObjectBase) error { query = `insert into cachereplies (id, inreplyto) values ($1, $2)` - _, err = db.Exec(query, obj.Id, e.Id) + _, err = config.DB.Exec(query, obj.Id, e.Id) if err != nil { return err } @@ -238,54 +44,13 @@ func WriteObjectReplyToCache(obj activitypub.ObjectBase) error { if len(obj.InReplyTo) < 1 { query := `insert into cachereplies (id, inreplyto) values ($1, $2)` - _, err := db.Exec(query, obj.Id, "") + _, err := config.DB.Exec(query, obj.Id, "") return err } return nil } -func WriteObjectReplyCache(obj activitypub.ObjectBase) error { - if obj.Replies != nil { - for _, e := range obj.Replies.OrderedItems { - - query := `select inreplyto from cachereplies where id=$1` - - rows, err := db.Query(query, obj.Id) - if err != nil { - return err - } - defer rows.Close() - - var inreplyto string - rows.Next() - err = rows.Scan(&inreplyto) - if err != nil { - return err - } else if inreplyto != "" { - return nil // TODO: error? - } - - query = `insert into cachereplies (id, inreplyto) values ($1, $2)` - - if _, err := db.Exec(query, e.Id, obj.Id); err != nil { - return err - } - - if res, err := IsObjectLocal(e.Id); err == nil && !res { - if _, err := WriteObjectToCache(e); err != nil { - return err - } - } else if err != nil { - return err - } - - } - } - - return nil -} - func WriteActorToCache(actorID string) error { actor, err := webfinger.FingerActor(actorID) if err != nil { @@ -298,30 +63,7 @@ func WriteActorToCache(actorID string) error { } for _, e := range collection.OrderedItems { - if _, err := WriteActorObjectToCache(e); err != nil { - return err - } - } - - return nil -} - -func DeleteActorCache(actorID string) error { - query := `select id from cacheactivitystream where id in (select id from cacheactivitystream where actor=$1)` - - rows, err := db.Query(query, actorID) - if err != nil { - return err - } - defer rows.Close() - - for rows.Next() { - var id string - if err := rows.Scan(&id); err != nil { - return err - } - - if err := DeleteObject(id); err != nil { + if _, err := activitypub.WriteActorObjectToCache(e); err != nil { return err } } diff --git a/db/database.go b/db/database.go index db7e715..5cc1335 100644 --- a/db/database.go +++ b/db/database.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "os" "regexp" - "sort" "strings" "time" @@ -18,10 +17,6 @@ import ( _ "github.com/lib/pq" ) -// TODO: merge these -var db *sql.DB -var DB *sql.DB - type NewsItem struct { Title string Content template.HTML @@ -49,43 +44,14 @@ func ConnectDB() error { } fmt.Println("Successfully connected DB") - db = _db - DB = _db + config.DB = _db return nil } // Close closes the database connection. func Close() error { - return db.Close() -} - -func CreateUniqueID(actor string) (string, error) { - var newID string - isUnique := false - for !isUnique { - newID = util.RandomID(8) - - query := "select id from activitystream where id=$1" - args := fmt.Sprintf("%s/%s/%s", config.Domain, actor, newID) - rows, err := db.Query(query, args) - if err != nil { - return "", err - } - - defer rows.Close() - - // reusing a variable here - // if we encounter a match, it'll get set to false causing the outer for loop to loop and to go through this all over again - // however if nothing is there, it'll remain true and exit the loop - isUnique = true - for rows.Next() { - isUnique = false - break - } - } - - return newID, nil + return config.DB.Close() } func RunDatabaseSchema() error { @@ -94,73 +60,14 @@ func RunDatabaseSchema() error { return err } - _, err = db.Exec(string(query)) + _, err = config.DB.Exec(string(query)) return err } -func GetActorFromDB(id string) (activitypub.Actor, error) { - var nActor activitypub.Actor - - query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return nActor, err - } - - var publicKeyPem string - defer rows.Close() - for rows.Next() { - if err := rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem); err != nil { - return nActor, err - } - } - - nActor.PublicKey, err = GetActorPemFromDB(publicKeyPem) - if err != nil { - return nActor, err - } - - if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { - if err := CreatePublicKeyFromPrivate(&nActor, publicKeyPem); err != nil { - return nActor, err - } - } - - return nActor, nil -} - -func GetActorByNameFromDB(name string) (activitypub.Actor, error) { - var nActor activitypub.Actor - - query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1` - - rows, err := db.Query(query, name) - if err != nil { - return nActor, err - } - - var publicKeyPem string - defer rows.Close() - for rows.Next() { - if err := rows.Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem); err != nil { - return nActor, err - } - } - - if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { - if err := CreatePublicKeyFromPrivate(&nActor, publicKeyPem); err != nil { - return nActor, err - } - } - - return nActor, nil -} - func CreateNewBoardDB(actor activitypub.Actor) (activitypub.Actor, error) { query := `insert into actor (type, id, name, preferedusername, inbox, outbox, following, followers, summary, restricted) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` - _, err := db.Exec(query, actor.Type, actor.Id, actor.Name, actor.PreferredUsername, actor.Inbox, actor.Outbox, actor.Following, actor.Followers, actor.Summary, actor.Restricted) + _, err := config.DB.Exec(query, actor.Type, actor.Id, actor.Name, actor.PreferredUsername, actor.Inbox, actor.Outbox, actor.Following, actor.Followers, actor.Summary, actor.Restricted) if err != nil { // TODO: board exists error @@ -171,7 +78,7 @@ func CreateNewBoardDB(actor activitypub.Actor) (activitypub.Actor, error) { for _, e := range actor.AuthRequirement { query = `insert into actorauth (type, board) values ($1, $2)` - if _, err := db.Exec(query, e, actor.Name); err != nil { + if _, err := config.DB.Exec(query, e, actor.Name); err != nil { return activitypub.Actor{}, err } } @@ -212,13 +119,13 @@ func CreateNewBoardDB(actor activitypub.Actor) (activitypub.Actor, error) { nverify.Type = "post" CreateBoardMod(nverify) - CreatePem(actor) + activitypub.CreatePem(actor) if actor.Name != "main" { var nObject activitypub.ObjectBase var nActivity activitypub.Activity - nActor, err := GetActorFromDB(config.Domain) + nActor, err := activitypub.GetActorFromDB(config.Domain) if err != nil { return actor, err } @@ -228,7 +135,7 @@ func CreateNewBoardDB(actor activitypub.Actor) (activitypub.Actor, error) { nActivity.Actor = &nActor nActivity.Object = &nObject - mActor, err := GetActorFromDB(actor.Id) + mActor, err := activitypub.GetActorFromDB(actor.Id) if err != nil { return actor, err } @@ -250,1109 +157,9 @@ func CreateNewBoardDB(actor activitypub.Actor) (activitypub.Actor, error) { return actor, nil } -func GetBoards() ([]activitypub.Actor, error) { - var board []activitypub.Actor - - query := `select type, id, name, preferedusername, inbox, outbox, following, followers FROM actor` - - rows, err := db.Query(query) - if err != nil { - return board, err - } - - defer rows.Close() - for rows.Next() { - var actor = new(activitypub.Actor) - - if err := rows.Scan(&actor.Type, &actor.Id, &actor.Name, &actor.PreferredUsername, &actor.Inbox, &actor.Outbox, &actor.Following, &actor.Followers); err != nil { - return board, err - } - - board = append(board, *actor) - } - - return board, nil -} - -func WriteObjectToDB(obj activitypub.ObjectBase) (activitypub.ObjectBase, error) { - id, err := CreateUniqueID(obj.Actor) - if err != nil { - return obj, err - } - - obj.Id = fmt.Sprintf("%s/%s", obj.Actor, id) - if len(obj.Attachment) > 0 { - if obj.Preview.Href != "" { - id, err := CreateUniqueID(obj.Actor) - if err != nil { - return obj, err - } - - obj.Preview.Id = fmt.Sprintf("%s/%s", obj.Actor, id) - obj.Preview.Published = time.Now().UTC() - obj.Preview.Updated = time.Now().UTC() - obj.Preview.AttributedTo = obj.Id - if err := WritePreviewToDB(*obj.Preview); err != nil { - return obj, err - } - } - - for i := range obj.Attachment { - id, err := CreateUniqueID(obj.Actor) - if err != nil { - return obj, err - } - - obj.Attachment[i].Id = fmt.Sprintf("%s/%s", obj.Actor, id) - obj.Attachment[i].Published = time.Now().UTC() - obj.Attachment[i].Updated = time.Now().UTC() - obj.Attachment[i].AttributedTo = obj.Id - WriteAttachmentToDB(obj.Attachment[i]) - WriteActivitytoDBWithAttachment(obj, obj.Attachment[i], *obj.Preview) - } - - } else { - if err := WriteActivitytoDB(obj); err != nil { - return obj, err - } - } - - if err := WriteObjectReplyToDB(obj); err != nil { - return obj, err - } - - err = WriteWalletToDB(obj) - return obj, err -} - -func WriteObjectUpdatesToDB(obj activitypub.ObjectBase) error { - query := `update activitystream set updated=$1 where id=$2` - - if _, err := db.Exec(query, time.Now().UTC().Format(time.RFC3339), obj.Id); err != nil { - return err - } - - query = `update cacheactivitystream set updated=$1 where id=$2` - - _, err := db.Exec(query, time.Now().UTC().Format(time.RFC3339), obj.Id) - return err -} - -func WriteObjectReplyToLocalDB(id string, replyto string) error { - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, id, replyto) - if err != nil { - return err - } - defer rows.Close() - - var nID string - rows.Next() - rows.Scan(&nID) - - if nID == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - if _, err := db.Exec(query, id, replyto); err != nil { - return err - } - } - - query = `select inreplyto from replies where id=$1` - - rows, err = db.Query(query, replyto) - if err != nil { - return err - } - defer rows.Close() - - for rows.Next() { - var val string - rows.Scan(&val) - if val == "" { - updated := time.Now().UTC().Format(time.RFC3339) - query := `update activitystream set updated=$1 where id=$2` - - if _, err := db.Exec(query, updated, replyto); err != nil { - return err - } - } - } - - return nil -} - -func WriteObjectReplyToDB(obj activitypub.ObjectBase) error { - for i, e := range obj.InReplyTo { - - if res, err := CheckIfObjectOP(obj.Id); err == nil && !res && i == 0 { - nType, err := GetObjectTypeDB(e.Id) - if err != nil { - return err - } - - if nType == "Archive" { - if err := UpdateObjectTypeDB(obj.Id, "Archive"); err != nil { - return err - } - } - } else if err != nil { - return err - } - - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, obj.Id, e.Id) - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - if err := rows.Scan(&id); err != nil { - return err - } - - if id == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - _, err := db.Exec(query, obj.Id, e.Id) - if err != nil { - return err - } - } - - update := true - for _, e := range obj.Option { - if e == "sage" || e == "nokosage" { - update = false - break - } - } - - if update { - if err := WriteObjectUpdatesToDB(e); err != nil { - return err - } - } - } - - if len(obj.InReplyTo) < 1 { - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, obj.Id, "") - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - rows.Scan(&id) - - if id == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - if _, err := db.Exec(query, obj.Id, ""); err != nil { - return err - } - } - } - - return nil -} - -func WriteActorObjectReplyToDB(obj activitypub.ObjectBase) error { - for _, e := range obj.InReplyTo { - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, obj.Id, e.Id) - if err != nil { - return err - } - - defer rows.Close() - - var id string - rows.Next() - rows.Scan(&id) - - if id == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - if _, err := db.Exec(query, obj.Id, e.Id); err != nil { - return err - } - } - } - - if len(obj.InReplyTo) < 1 { - query := `select id from replies where id=$1 and inreplyto=$2` - - rows, err := db.Query(query, obj.Id, "") - if err != nil { - return err - } - defer rows.Close() - - var id string - rows.Next() - rows.Scan(&id) - - if id == "" { - query := `insert into replies (id, inreplyto) values ($1, $2)` - - if _, err := db.Exec(query, obj.Id, ""); err != nil { - return err - } - } - } - - return nil -} - -func WriteWalletToDB(obj activitypub.ObjectBase) error { - for _, e := range obj.Option { - if e == "wallet" { - for _, e := range obj.Wallet { - query := `insert into wallet (id, type, address) values ($1, $2, $3)` - - if _, err := db.Exec(query, obj.Id, e.Type, e.Address); err != nil { - return err - } - } - - return nil - } - } - return nil -} - -func WriteActivitytoDB(obj activitypub.ObjectBase) error { - obj.Name = util.EscapeString(obj.Name) - obj.Content = util.EscapeString(obj.Content) - obj.AttributedTo = util.EscapeString(obj.AttributedTo) - - query := `insert into activitystream (id, type, name, content, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)` - - _, err := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - return err -} - -func WriteActivitytoDBWithAttachment(obj activitypub.ObjectBase, attachment activitypub.ObjectBase, preview activitypub.NestedObjectBase) { - - obj.Name = util.EscapeString(obj.Name) - obj.Content = util.EscapeString(obj.Content) - obj.AttributedTo = util.EscapeString(obj.AttributedTo) - - query := `insert into activitystream (id, type, name, content, attachment, preview, published, updated, attributedto, actor, tripcode, sensitive) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` - - _, e := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Content, attachment.Id, preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - - if e != nil { - fmt.Println("error inserting new activity with attachment") - panic(e) - } -} - -func WriteAttachmentToDB(obj activitypub.ObjectBase) { - query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, e := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) - - if e != nil { - fmt.Println("error inserting new attachment") - panic(e) - } -} - -func WritePreviewToDB(obj activitypub.NestedObjectBase) error { - query := `insert into activitystream (id, type, name, href, published, updated, attributedTo, mediatype, size) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)` - - _, err := db.Exec(query, obj.Id, obj.Type, obj.Name, obj.Href, obj.Published, obj.Updated, obj.AttributedTo, obj.MediaType, obj.Size) - return err -} - -func GetActivityFromDB(id string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var nActor activitypub.Actor - var result []activitypub.ObjectBase - - nColl.Actor = &nActor - - query := `select actor, id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 order by updated asc` - - rows, err := db.Query(query, id) - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&nColl.Actor.Id, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - var err error - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetObjectFromDBPage(id string, page int) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select count (x.id) over(), x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 15 offset $2` - - rows, err := db.Query(query, id, page*15) - if err != nil { - return nColl, err - } - - var count int - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&count, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - var err error - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDBLimit(post, 5) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.TotalItems = count - nColl.OrderedItems = result - - return nColl, nil -} - -func GetActorObjectCollectionFromDB(actorId string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' order by updated desc` - - rows, err := db.Query(query, actorId) - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetObjectFromDB(id string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 order by updated desc` - - rows, err := db.Query(query, id) - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetObjectFromDBFromID(id string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id like $1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id like $1 and type='Note') as x order by x.updated` - - re := regexp.MustCompile(`f(\w+)\-`) - match := re.FindStringSubmatch(id) - - if len(match) > 0 { - re := regexp.MustCompile(`(.+)\-`) - id = re.ReplaceAllString(id, "") - id = "%" + match[1] + "/" + id - } - - rows, err := db.Query(query, id) - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetObjectFromDBCatalog(id string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc limit 165` - - rows, err := db.Query(query, id) - - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var replies activitypub.CollectionBase - - post.Replies = &replies - - post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) - - if err != nil { - return nColl, err - } - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetObjectByIDFromDB(postID string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and (type='Note' or type='Archive') union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and (type='Note' or type='Archive')) as x` - - rows, err := db.Query(query, postID) - if err != nil { - return nColl, err - } - defer rows.Close() - - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - actor, err = GetActorFromDB(actor.Id) - if err != nil { - return nColl, err - } - - post.Actor = actor.Id - - nColl.Actor = &actor - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetInReplyToDB(parent activitypub.ObjectBase) ([]activitypub.ObjectBase, error) { - var result []activitypub.ObjectBase - - query := `select inreplyto from replies where id =$1` - - rows, err := db.Query(query, parent.Id) - if err != nil { - return result, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - - if err := rows.Scan(&post.Id); err != nil { - return result, err - } - - result = append(result, post) - } - - return result, nil -} - -func GetObjectRepliesDBLimit(parent activitypub.ObjectBase, limit int) (*activitypub.CollectionBase, int, int, error) { - var nColl activitypub.CollectionBase - var result []activitypub.ObjectBase - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published desc limit $2` - - rows, err := db.Query(query, parent.Id, limit) - if err != nil { - return nil, 0, 0, err - } - - var postCount int - var attachCount int - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - if err := rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return &nColl, postCount, attachCount, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - - post.Replies, postCnt, imgCnt, err = GetObjectRepliesRepliesDB(post) - if err != nil { - return &nColl, postCount, attachCount, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return &nColl, postCount, attachCount, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return &nColl, postCount, attachCount, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - sort.Sort(activitypub.ObjectBaseSortAsc(nColl.OrderedItems)) - - return &nColl, postCount, attachCount, nil -} - -func GetObjectRepliesDB(parent activitypub.ObjectBase) (*activitypub.CollectionBase, int, int, error) { - var nColl activitypub.CollectionBase - var result []activitypub.ObjectBase - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive')) as x order by x.published asc` - - rows, err := db.Query(query, parent.Id) - if err != nil { - return nil, 0, 0, err - } - - var postCount int - var attachCount int - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - if err := rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return &nColl, postCount, attachCount, err - } - - post.Actor = actor.Id - - var postCnt int - var imgCnt int - - post.Replies, postCnt, imgCnt, err = GetObjectRepliesRepliesDB(post) - if err != nil { - return &nColl, postCount, attachCount, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return &nColl, postCount, attachCount, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return &nColl, postCount, attachCount, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return &nColl, postCount, attachCount, nil -} - -func GetObjectRepliesReplies(parent activitypub.ObjectBase) (*activitypub.CollectionBase, int, int, error) { - var nColl activitypub.CollectionBase - var result []activitypub.ObjectBase - - query := `select id, name, content, type, published, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id in (select id from replies where inreplyto=$1) and (type='Note' or type='Archive') order by updated asc` - - rows, err := db.Query(query, parent.Id) - if err != nil { - return &nColl, 0, 0, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return &nColl, 0, 0, err - } - - post.Actor = actor.Id - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return &nColl, 0, 0, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return &nColl, 0, 0, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return &nColl, 0, 0, nil -} - -func GetObjectRepliesRepliesDB(parent activitypub.ObjectBase) (*activitypub.CollectionBase, int, int, error) { - var nColl activitypub.CollectionBase - var result []activitypub.ObjectBase - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over(), x.id, x.name, x.content, x.type, x.published, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select * from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select * from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x order by x.published asc` - - rows, err := db.Query(query, parent.Id) - if err != nil { - return &nColl, 0, 0, err - } - - var postCount int - var attachCount int - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - post.InReplyTo = append(post.InReplyTo, parent) - - if err := rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return &nColl, postCount, attachCount, err - } - - post.Actor = actor.Id - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return &nColl, postCount, attachCount, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return &nColl, postCount, attachCount, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return &nColl, postCount, attachCount, nil -} - -func CheckIfObjectOP(id string) (bool, error) { - var count int - - query := `select count(id) from replies where inreplyto='' and id=$1 ` - - rows, err := db.Query(query, id) - if err != nil { - return false, err - } - defer rows.Close() - - rows.Next() - if err := rows.Scan(&count); err != nil { - return false, err - } - - if count > 0 { - return true, nil - } - - return false, nil -} - -func GetObjectRepliesCount(parent activitypub.ObjectBase) (int, int, error) { - var countId int - var countImg int - - query := `select count(x.id) over(), sum(case when RTRIM(x.attachment) = '' then 0 else 1 end) over() from (select id, attachment from activitystream where id in (select id from replies where inreplyto=$1) and type='Note' union select id, attachment from cacheactivitystream where id in (select id from replies where inreplyto=$1) and type='Note') as x` - - rows, err := db.Query(query, parent.Id) - if err != nil { - return 0, 0, err - } - - defer rows.Close() - - for rows.Next() { - err = rows.Scan(&countId, &countImg) - } - - return countId, countImg, err -} - -func GetObjectAttachment(id string) ([]activitypub.ObjectBase, error) { - var attachments []activitypub.ObjectBase - - query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` - - rows, err := db.Query(query, id) - if err != nil { - return attachments, err - } - - defer rows.Close() - for rows.Next() { - var attachment = new(activitypub.ObjectBase) - - if err := rows.Scan(&attachment.Id, &attachment.Type, &attachment.Name, &attachment.Href, &attachment.MediaType, &attachment.Size, &attachment.Published); err != nil { - return attachments, err - } - - attachments = append(attachments, *attachment) - } - - return attachments, nil -} - -func GetObjectPreview(id string) (*activitypub.NestedObjectBase, error) { - var preview activitypub.NestedObjectBase - - query := `select x.id, x.type, x.name, x.href, x.mediatype, x.size, x.published from (select id, type, name, href, mediatype, size, published from activitystream where id=$1 union select id, type, name, href, mediatype, size, published from cacheactivitystream where id=$1) as x` - - rows, err := db.Query(query, id) - if err != nil { - return nil, err - } - - defer rows.Close() - for rows.Next() { - if err := rows.Scan(&preview.Id, &preview.Type, &preview.Name, &preview.Href, &preview.MediaType, &preview.Size, &preview.Published); err != nil { - return nil, err - } - } - - return &preview, nil -} - -func GetObjectPostsTotalDB(actor activitypub.Actor) (int, error) { - count := 0 - query := `select count(id) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note')` - - rows, err := db.Query(query, actor.Id) - if err != nil { - return count, err - } - - defer rows.Close() - for rows.Next() { - if err := rows.Scan(&count); err != nil { - return count, err - } - } - - return count, nil -} - -func GetObjectImgsTotalDB(actor activitypub.Actor) (int, error) { - count := 0 - query := `select count(attachment) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note' )` - - rows, err := db.Query(query, actor.Id) - if err != nil { - return count, err - } - defer rows.Close() - - for rows.Next() { - if err := rows.Scan(&count); err != nil { - return count, err - } - } - - return count, nil -} - -func DeletePreviewFromFile(id string) error { - query := `select href from activitystream where id in (select preview from activitystream where id=$1)` - - rows, err := db.Query(query, id) - if err != nil { - return err - } - defer rows.Close() - - for rows.Next() { - var href string - - if err := rows.Scan(&href); err != nil { - return err - } - - href = strings.Replace(href, config.Domain+"/", "", 1) - - if href != "static/notfound.png" { - _, err = os.Stat(href) - if err == nil { - return os.Remove(href) - } - return err - } - } - - return nil -} - func RemovePreviewFromFile(id string) error { query := `select href from activitystream where id in (select preview from activitystream where id=$1)` - rows, err := db.Query(query, id) + rows, err := config.DB.Query(query, id) if err != nil { return err } @@ -1376,416 +183,7 @@ func RemovePreviewFromFile(id string) error { } } - return DeletePreviewFromDB(id) -} - -func DeleteAttachmentFromFile(id string) error { - query := `select href from activitystream where id in (select attachment from activitystream where id=$1)` - - rows, err := db.Query(query, id) - if err != nil { - return err - } - defer rows.Close() - - for rows.Next() { - var href string - - if err := rows.Scan(&href); err != nil { - return err - } - - href = strings.Replace(href, config.Domain+"/", "", 1) - - if href != "static/notfound.png" { - _, err = os.Stat(href) - if err == nil { - os.Remove(href) - } - return err - } - } - - return nil -} - -func TombstonePreviewRepliesFromDB(id string) error { - query := `select id from activitystream where id in (select id from replies where inreplyto=$1)` - - rows, err := db.Query(query, id) - if err != nil { - return err - } - - defer rows.Close() - for rows.Next() { - var attachment string - - if err := rows.Scan(&attachment); err != nil { - return err - } - - if err := DeletePreviewFromFile(attachment); err != nil { - return err - } - - if err := TombstonePreviewFromDB(attachment); err != nil { - return err - } - } - - return nil -} - -func TombstoneAttachmentRepliesFromDB(id string) error { - query := `select id from activitystream where id in (select id from replies where inreplyto=$1)` - - rows, err := db.Query(query, id) - if err != nil { - return err - } - - defer rows.Close() - for rows.Next() { - var attachment string - - if err := rows.Scan(&attachment); err != nil { - return err - } - - if err := DeleteAttachmentFromFile(attachment); err != nil { - return err - } - - if err := TombstoneAttachmentFromDB(attachment); err != nil { - return err - } - } - - return nil -} - -func TombstoneAttachmentFromDB(id string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from activitystream where id=$3)` - - if _, err := db.Exec(query, config.Domain+"/static/notfound.png", datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` - - _, err := db.Exec(query, config.Domain+"/static/notfound.png", datetime, id) - return err -} - -func DeleteAttachmentFromDB(id string) error { - query := `delete from activitystream where id in (select attachment from activitystream where id=$1)` - - if _, err := db.Exec(query, id); err != nil { - return err - } - - query = `delete from cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` - - _, err := db.Exec(query, id) - return err -} - -func TombstonePreviewFromDB(id string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from activitystream where id=$3)` - - if _, err := db.Exec(query, config.Domain+"/static/notfound.png", datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type='Tombstone', mediatype='image/png', href=$1, name='', content='', attributedto='deleted', deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` - - _, err := db.Exec(query, config.Domain+"/static/notfound.png", datetime, id) - return err -} - -func DeletePreviewFromDB(id string) error { - query := `delete from activitystream where id=$1` - - if _, err := db.Exec(query, id); err != nil { - return err - } - - query = `delete from cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` - - _, err := db.Exec(query, id) - return err -} - -func DeleteObjectRepliedTo(id string) error { - query := `delete from replies where id=$1` - _, err := db.Exec(query, id) - return err -} - -func TombstoneObjectFromDB(id string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - query := `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` - - if _, err := db.Exec(query, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` - - _, err := db.Exec(query, datetime, id) - return err -} - -func DeleteObjectFromDB(id string) error { - var query = `delete from activitystream where id=$1` - - if _, err := db.Exec(query, id); err != nil { - return err - } - - query = `delete from cacheactivitystream where id=$1` - - _, err := db.Exec(query, id) - return err -} - -func DeleteObjectsInReplyTo(id string) error { - query := `delete from replies where id in (select id from replies where inreplyto=$1)` - - _, err := db.Exec(query, id) - return err -} - -func TombstoneObjectRepliesFromDB(id string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` - - if _, err := db.Exec(query, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id in (select id from replies where inreplyto=$2)` - - _, err := db.Exec(query, datetime, id) - return err -} - -func SetAttachmentFromDB(id string, _type string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id=$3)` - - if _, err := db.Exec(query, _type, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id=$3)` - - _, err := db.Exec(query, _type, datetime, id) - return err -} - -func SetAttachmentRepliesFromDB(id string, _type string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type=$1, deleted=$2 where id in (select attachment from activitystream where id in (select id from replies where inreplyto=$3))` - - if _, err := db.Exec(query, _type, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select attachment from cacheactivitystream where id in (select id from replies where inreplyto=$3))` - - _, err := db.Exec(query, _type, datetime, id) - return err -} - -func SetPreviewFromDB(id string, _type string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id=$3)` - - if _, err := db.Exec(query, _type, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id=$3)` - - _, err := db.Exec(query, _type, datetime, id) - return err -} - -func SetPreviewRepliesFromDB(id string, _type string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type=$1, deleted=$2 where id in (select preview from activitystream where id in (select id from replies where inreplyto=$3))` - - if _, err := db.Exec(query, _type, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select preview from cacheactivitystream where id in (select id from replies where inreplyto=$3))` - - _, err := db.Exec(query, _type, datetime, id) - return err -} - -func SetObjectFromDB(id string, _type string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - query := `update activitystream set type=$1, deleted=$2 where id=$3` - - if _, err := db.Exec(query, _type, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type=$1, deleted=$2 where id=$3` - - _, err := db.Exec(query, _type, datetime, id) - return err -} - -func SetObjectRepliesFromDB(id string, _type string) error { - datetime := time.Now().UTC().Format(time.RFC3339) - - var query = `update activitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` - if _, err := db.Exec(query, _type, datetime, id); err != nil { - return err - } - - query = `update cacheactivitystream set type=$1, deleted=$2 where id in (select id from replies where inreplyto=$3)` - _, err := db.Exec(query, _type, datetime, id) - return err -} - -func SetObject(id string, _type string) error { - if err := SetAttachmentFromDB(id, _type); err != nil { - return err - } - - if err := SetPreviewFromDB(id, _type); err != nil { - return err - } - - return SetObjectFromDB(id, _type) -} - -func SetObjectAndReplies(id string, _type string) error { - if err := SetAttachmentFromDB(id, _type); err != nil { - return err - } - - if err := SetPreviewFromDB(id, _type); err != nil { - return err - } - - if err := SetObjectRepliesFromDB(id, _type); err != nil { - return err - } - - if err := SetAttachmentRepliesFromDB(id, _type); err != nil { - return err - } - - if err := SetPreviewRepliesFromDB(id, _type); err != nil { - return err - } - - return SetObjectFromDB(id, _type) -} - -func DeleteObject(id string) error { - if err := DeleteReportActivity(id); err != nil { - return err - } - - if err := DeleteAttachmentFromFile(id); err != nil { - return err - } - - if err := DeleteAttachmentFromDB(id); err != nil { - return err - } - - if err := DeletePreviewFromFile(id); err != nil { - return err - } - - if err := DeletePreviewFromDB(id); err != nil { - return err - } - - if err := DeleteObjectFromDB(id); err != nil { - return err - } - - return DeleteObjectRepliedTo(id) -} - -func TombstoneObject(id string) error { - if err := DeleteReportActivity(id); err != nil { - return err - } - - if err := DeleteAttachmentFromFile(id); err != nil { - return err - } - - if err := TombstoneAttachmentFromDB(id); err != nil { - return err - } - - if err := DeletePreviewFromFile(id); err != nil { - return err - } - - if err := TombstonePreviewFromDB(id); err != nil { - return err - } - - return TombstoneObjectFromDB(id) -} - -func TombstoneObjectAndReplies(id string) error { - if err := DeleteReportActivity(id); err != nil { - return err - } - - if err := DeleteAttachmentFromFile(id); err != nil { - return err - } - - if err := TombstoneAttachmentFromDB(id); err != nil { - return err - } - - if err := DeletePreviewFromFile(id); err != nil { - return err - } - - if err := TombstonePreviewFromDB(id); err != nil { - return err - } - - if err := TombstoneObjectRepliesFromDB(id); err != nil { - return err - } - - if err := TombstoneAttachmentRepliesFromDB(id); err != nil { - return err - } - - if err := TombstonePreviewRepliesFromDB(id); err != nil { - return err - } - - return TombstoneObjectFromDB(id) + return activitypub.DeletePreviewFromDB(id) } func GetRandomCaptcha() (string, error) { @@ -1793,7 +191,7 @@ func GetRandomCaptcha() (string, error) { query := `select identifier from verification where type='captcha' order by random() limit 1` - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return verify, err } @@ -1810,7 +208,7 @@ func GetRandomCaptcha() (string, error) { func GetCaptchaTotal() (int, error) { query := `select count(*) from verification where type='captcha'` - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return 0, err } @@ -1830,7 +228,7 @@ func GetCaptchaTotal() (int, error) { func GetCaptchaCodeDB(verify string) (string, error) { query := `select code from verification where identifier=$1 limit 1` - rows, err := db.Query(query, verify) + rows, err := config.DB.Query(query, verify) if err != nil { return "", err } @@ -1846,33 +244,10 @@ func GetCaptchaCodeDB(verify string) (string, error) { return code, nil } -func GetActorAuth(actor string) ([]string, error) { - var auth []string - - query := `select type from actorauth where board=$1` - - rows, err := db.Query(query, actor) - if err != nil { - return auth, err - } - defer rows.Close() - - for rows.Next() { - var e string - if err := rows.Scan(&e); err != nil { - return auth, err - } - - auth = append(auth, e) - } - - return auth, nil -} - func DeleteCaptchaCodeDB(verify string) error { query := `delete from verification where identifier=$1` - _, err := db.Exec(query, verify) + _, err := config.DB.Exec(query, verify) if err != nil { return err } @@ -1880,58 +255,6 @@ func DeleteCaptchaCodeDB(verify string) error { return os.Remove("./" + verify) } -func GetActorReportedTotal(id string) (int, error) { - query := `select count(id) from reported where board=$1` - - rows, err := db.Query(query, id) - if err != nil { - return 0, err - } - - defer rows.Close() - - var count int - for rows.Next() { - rows.Scan(&count) - } - - return count, nil -} - -func GetActorReportedDB(id string) ([]activitypub.ObjectBase, error) { - var nObj []activitypub.ObjectBase - - query := `select id, count, reason from reported where board=$1` - - rows, err := db.Query(query, id) - if err != nil { - return nObj, err - } - - defer rows.Close() - - for rows.Next() { - var obj activitypub.ObjectBase - - rows.Scan(&obj.Id, &obj.Size, &obj.Content) - - nObj = append(nObj, obj) - } - - return nObj, nil -} - -func MarkObjectSensitive(id string, sensitive bool) error { - var query = `update activitystream set sensitive=$1 where id=$2` - if _, err := db.Exec(query, sensitive, id); err != nil { - return err - } - - query = `update cacheactivitystream set sensitive=$1 where id=$2` - _, err := db.Exec(query, sensitive, id) - return err -} - //if limit less than 1 return all news items func GetNewsFromDB(limit int) ([]NewsItem, error) { var news []NewsItem @@ -1946,9 +269,9 @@ func GetNewsFromDB(limit int) ([]NewsItem, error) { var rows *sql.Rows var err error if limit > 0 { - rows, err = db.Query(query, limit) + rows, err = config.DB.Query(query, limit) } else { - rows, err = db.Query(query) + rows, err = config.DB.Query(query) } if err != nil { @@ -1977,7 +300,7 @@ func GetNewsItemFromDB(timestamp int) (NewsItem, error) { var content string query := `select title, content, time from newsItem where time=$1 limit 1` - rows, err := db.Query(query, timestamp) + rows, err := config.DB.Query(query, timestamp) if err != nil { return news, err } @@ -1997,48 +320,21 @@ func GetNewsItemFromDB(timestamp int) (NewsItem, error) { func deleteNewsItemFromDB(timestamp int) error { query := `delete from newsItem where time=$1` - _, err := db.Exec(query, timestamp) + _, err := config.DB.Exec(query, timestamp) return err } func WriteNewsToDB(news NewsItem) error { query := `insert into newsItem (title, content, time) values ($1, $2, $3)` - _, err := db.Exec(query, news.Title, news.Content, time.Now().Unix()) - return err -} - -func GetActorAutoSubscribeDB(id string) (bool, error) { - query := `select autosubscribe from actor where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return false, err - } - - var subscribed bool - defer rows.Close() - rows.Next() - err = rows.Scan(&subscribed) - return subscribed, err -} - -func SetActorAutoSubscribeDB(id string) error { - current, err := GetActorAutoSubscribeDB(id) - if err != nil { - return err - } - - query := `update actor set autosubscribe=$1 where id=$2` - - _, err = db.Exec(query, !current, id) + _, err := config.DB.Exec(query, news.Title, news.Content, time.Now().Unix()) return err } func AddInstanceToInactiveDB(instance string) error { query := `select timestamp from inactive where instance=$1` - rows, err := db.Query(query, instance) + rows, err := config.DB.Query(query, instance) if err != nil { return err } @@ -2051,7 +347,7 @@ func AddInstanceToInactiveDB(instance string) error { if timeStamp == "" { query := `insert into inactive (instance, timestamp) values ($1, $2)` - _, err := db.Exec(query, instance, time.Now().UTC().Format(time.RFC3339)) + _, err := config.DB.Exec(query, instance, time.Now().UTC().Format(time.RFC3339)) return err } @@ -2060,12 +356,12 @@ func AddInstanceToInactiveDB(instance string) error { } query = `delete from following where following like $1` - if _, err := db.Exec(query, "%"+instance+"%"); err != nil { + if _, err := config.DB.Exec(query, "%"+instance+"%"); err != nil { return err } query = `delete from follower where follower like $1` - if _, err = db.Exec(query, "%"+instance+"%"); err != nil { + if _, err = config.DB.Exec(query, "%"+instance+"%"); err != nil { return err } @@ -2075,7 +371,7 @@ func AddInstanceToInactiveDB(instance string) error { func DeleteInstanceFromInactiveDB(instance string) error { query := `delete from inactive where instance=$1` - _, err := db.Exec(query, instance) + _, err := config.DB.Exec(query, instance) return err } @@ -2090,19 +386,19 @@ func IsInactiveTimestamp(timeStamp string) bool { func ArchivePosts(actor activitypub.Actor) error { if actor.Id != "" && actor.Id != config.Domain { - col, err := GetAllActorArchiveDB(actor.Id, 165) + col, err := activitypub.GetAllActorArchiveDB(actor.Id, 165) if err != nil { return err } for _, e := range col.OrderedItems { for _, k := range e.Replies.OrderedItems { - if err := UpdateObjectTypeDB(k.Id, "Archive"); err != nil { + if err := activitypub.UpdateObjectTypeDB(k.Id, "Archive"); err != nil { return err } } - if err := UpdateObjectTypeDB(e.Id, "Archive"); err != nil { + if err := activitypub.UpdateObjectTypeDB(e.Id, "Archive"); err != nil { return err } } @@ -2111,164 +407,20 @@ func ArchivePosts(actor activitypub.Actor) error { return nil } -func GetAllActorArchiveDB(id string, offset int) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select x.id, x.updated from (select id, updated from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type='Note' union select id, updated from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note' union select id, updated from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type='Note') as x order by x.updated desc offset $2` - - rows, err := db.Query(query, id, offset) - if err != nil { - return nColl, err - } - defer rows.Close() - - for rows.Next() { - var post activitypub.ObjectBase - - if err := rows.Scan(&post.Id, &post.Updated); err != nil { - return nColl, err - } - - post.Replies, _, _, err = GetObjectRepliesDB(post) - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetActorCollectionDBType(actorId string, nType string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc` - - rows, err := db.Query(query, actorId, nType) - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var replies activitypub.CollectionBase - - post.Replies = &replies - - var err error - post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) - if err != nil { - return nColl, err - } - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetActorCollectionDBTypeLimit(actorId string, nType string, limit int) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor=$1 and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2 union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where actor in (select following from following where id=$1) and id in (select id from replies where inreplyto='') and type=$2) as x order by x.updated desc limit $3` - - rows, err := db.Query(query, actorId, nType, limit) - if err != nil { - return nColl, err - } - - defer rows.Close() - for rows.Next() { - var post activitypub.ObjectBase - var actor activitypub.Actor - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - var replies activitypub.CollectionBase - - post.Replies = &replies - - var err error - post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) - if err != nil { - return nColl, err - } - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.OrderedItems = result - - return nColl, nil -} - -func UpdateObjectTypeDB(id string, nType string) error { - query := `update activitystream set type=$2 where id=$1 and type !='Tombstone'` - if _, err := db.Exec(query, id, nType); err != nil { - return err - } - - query = `update cacheactivitystream set type=$2 where id=$1 and type !='Tombstone'` - _, err := db.Exec(query, id, nType) - return err -} - func UnArchiveLast(actorId string) error { - col, err := GetActorCollectionDBTypeLimit(actorId, "Archive", 1) + col, err := activitypub.GetActorCollectionDBTypeLimit(actorId, "Archive", 1) if err != nil { return err } for _, e := range col.OrderedItems { for _, k := range e.Replies.OrderedItems { - if err := UpdateObjectTypeDB(k.Id, "Note"); err != nil { + if err := activitypub.UpdateObjectTypeDB(k.Id, "Note"); err != nil { return err } } - if err := UpdateObjectTypeDB(e.Id, "Note"); err != nil { + if err := activitypub.UpdateObjectTypeDB(e.Id, "Note"); err != nil { return err } } @@ -2276,43 +428,6 @@ func UnArchiveLast(actorId string) error { return nil } -func SetObjectType(id string, nType string) error { - col, err := GetObjectFromDB(id) - if err != nil { - return err - } - - for _, e := range col.OrderedItems { - for _, k := range e.Replies.OrderedItems { - if err := UpdateObjectTypeDB(k.Id, nType); err != nil { - return err - } - } - - if err := UpdateObjectTypeDB(e.Id, nType); err != nil { - return err - } - } - - return nil -} - -func GetObjectTypeDB(id string) (string, error) { - query := `select type from activitystream where id=$1 union select type from cacheactivitystream where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return "", err - } - defer rows.Close() - - var nType string - rows.Next() - rows.Scan(&nType) - - return nType, nil -} - func IsReplyInThread(inReplyTo string, id string) (bool, error) { obj, _, err := webfinger.CheckValidActivity(inReplyTo) if err != nil { @@ -2328,24 +443,6 @@ func IsReplyInThread(inReplyTo string, id string) (bool, error) { return false, nil } -func GetActorsFollowPostFromId(actors []string, id string) (activitypub.Collection, error) { - var collection activitypub.Collection - - for _, e := range actors { - tempCol, err := GetObjectByIDFromDB(e + "/" + id) - if err != nil { - return collection, err - } - - if len(tempCol.OrderedItems) > 0 { - collection = tempCol - return collection, nil - } - } - - return collection, nil -} - func IsReplyToOP(op string, link string) (string, bool, error) { if op == link { return link, true, nil @@ -2362,7 +459,7 @@ func IsReplyToOP(op string, link string) (string, bool, error) { query := `select id from replies where id like $1 and inreplyto=$2` - rows, err := db.Query(query, link, op) + rows, err := config.DB.Query(query, link, op) if err != nil { return op, false, err } @@ -2380,7 +477,7 @@ func IsReplyToOP(op string, link string) (string, bool, error) { func GetReplyOP(link string) (string, error) { query := `select id from replies where id in (select inreplyto from replies where id=$1) and inreplyto=''` - rows, err := db.Query(query, link) + rows, err := config.DB.Query(query, link) if err != nil { return "", err } @@ -2395,7 +492,7 @@ func GetReplyOP(link string) (string, error) { func StartupArchive() error { for _, e := range FollowingBoards { - actor, err := GetActorFromDB(e.Id) + actor, err := activitypub.GetActorFromDB(e.Id) if err != nil { return err } @@ -2419,7 +516,7 @@ func CheckInactiveInstances() (map[string]string, error) { instances := make(map[string]string) query := `select following from following` - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return instances, err } @@ -2435,7 +532,7 @@ func CheckInactiveInstances() (map[string]string, error) { } query = `select follower from follower` - rows, err = db.Query(query) + rows, err = config.DB.Query(query) if err != nil { return instances, err } @@ -2474,7 +571,7 @@ func CheckInactiveInstances() (map[string]string, error) { func GetAdminAuth() (string, string, error) { query := fmt.Sprintf("select identifier, code from boardaccess where board='%s' and type='admin'", config.Domain) - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return "", "", err } @@ -2488,55 +585,12 @@ func GetAdminAuth() (string, string, error) { return code, identifier, err } -func UpdateObjectWithPreview(id string, preview string) error { - query := `update activitystream set preview=$1 where attachment=$2` - - _, err := db.Exec(query, preview, id) - return err -} - -func GetObjectsWithoutPreviewsCallback(callback func(id string, href string, mediatype string, name string, size int, published time.Time) error) error { - 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) - if err != nil { - return err - } - defer rows.Close() - - for rows.Next() { - var id string - var href string - var mediatype string - var name string - var size int - var published time.Time - - if err := rows.Scan(&id, &href, &mediatype, &name, &size, &published); err != nil { - return err - } - - if err := callback(id, href, mediatype, name, size, published); err != nil { - return err - } - } - - return nil -} - -func AddFollower(id string, follower string) error { - query := `insert into follower (id, follower) values ($1, $2)` - - _, err := db.Exec(query, id, follower) - return err -} - func IsHashBanned(hash string) (bool, error) { var h string query := `select hash from bannedmedia where hash=$1` - rows, err := db.Query(query, hash) + rows, err := config.DB.Query(query, hash) if err != nil { return true, err } diff --git a/db/follow.go b/db/follow.go index 0919e30..1aa9965 100644 --- a/db/follow.go +++ b/db/follow.go @@ -17,134 +17,6 @@ import ( _ "github.com/lib/pq" ) -func GetActorFollowing(w http.ResponseWriter, id string) error { - var following activitypub.Collection - var err error - - following.AtContext.Context = "https://www.w3.org/ns/activitystreams" - following.Type = "Collection" - following.TotalItems, _, err = GetActorFollowTotal(id) - if err != nil { - return err - } - - following.Items, err = GetActorFollowingDB(id) - if err != nil { - return err - } - - enc, _ := json.MarshalIndent(following, "", "\t") - w.Header().Set("Content-Type", config.ActivityStreams) - _, err = w.Write(enc) - - return err -} - -func GetActorFollowers(w http.ResponseWriter, id string) error { - var following activitypub.Collection - var err error - - following.AtContext.Context = "https://www.w3.org/ns/activitystreams" - following.Type = "Collection" - _, following.TotalItems, err = GetActorFollowTotal(id) - if err != nil { - return err - } - - following.Items, err = GetActorFollowDB(id) - if err != nil { - return err - } - - enc, _ := json.MarshalIndent(following, "", "\t") - w.Header().Set("Content-Type", config.ActivityStreams) - _, err = w.Write(enc) - return err -} - -func GetActorFollowingDB(id string) ([]activitypub.ObjectBase, error) { - var followingCollection []activitypub.ObjectBase - query := `select following from following where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return followingCollection, err - } - defer rows.Close() - - for rows.Next() { - var obj activitypub.ObjectBase - - if err := rows.Scan(&obj.Id); err != nil { - return followingCollection, err - } - - followingCollection = append(followingCollection, obj) - } - - return followingCollection, nil -} - -func GetActorFollowDB(id string) ([]activitypub.ObjectBase, error) { - var followerCollection []activitypub.ObjectBase - - query := `select follower from follower where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return followerCollection, err - } - defer rows.Close() - - for rows.Next() { - var obj activitypub.ObjectBase - - if err := rows.Scan(&obj.Id); err != nil { - return followerCollection, err - } - - followerCollection = append(followerCollection, obj) - } - - return followerCollection, nil -} - -func GetActorFollowTotal(id string) (int, int, error) { - var following int - var followers int - - query := `select count(following) from following where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return 0, 0, err - } - defer rows.Close() - - for rows.Next() { - if err := rows.Scan(&following); err != nil { - return following, 0, err - } - } - - query = `select count(follower) from follower where id=$1` - - rows, err = db.Query(query, id) - if err != nil { - return 0, 0, err - } - defer rows.Close() - - for rows.Next() { - if err := rows.Scan(&followers); err != nil { - return following, followers, err - } - - } - - return following, followers, nil -} - func AcceptFollow(activity activitypub.Activity) activitypub.Activity { var accept activitypub.Activity accept.AtContext.Context = activity.AtContext.Context @@ -164,95 +36,11 @@ func AcceptFollow(activity activitypub.Activity) activitypub.Activity { return accept } -func RejectActivity(activity activitypub.Activity) activitypub.Activity { - var accept activitypub.Activity - accept.AtContext.Context = activity.AtContext.Context - accept.Type = "Reject" - var nObj activitypub.ObjectBase - accept.Object = &nObj - var nActor activitypub.Actor - accept.Actor = &nActor - accept.Actor.Id = activity.Object.Actor - accept.Object.Actor = activity.Actor.Id - var nNested activitypub.NestedObjectBase - accept.Object.Object = &nNested - accept.Object.Object.Actor = activity.Object.Actor - accept.Object.Object.Type = "Follow" - accept.To = append(accept.To, activity.Actor.Id) - - return accept -} - -func IsAlreadyFollowing(actor string, follow string) (bool, error) { - followers, err := GetActorFollowingDB(actor) - if err != nil { - return false, err - } - - for _, e := range followers { - if e.Id == follow { - return true, nil - } - } - - return false, nil -} - -func IsAlreadyFollower(actor string, follow string) (bool, error) { - followers, err := GetActorFollowDB(actor) - if err != nil { - return false, err - } - - for _, e := range followers { - if e.Id == follow { - return true, nil - } - } - - return false, nil -} - -func SetActorFollowerDB(activity activitypub.Activity) (activitypub.Activity, error) { - var query string - alreadyFollow, err := IsAlreadyFollower(activity.Actor.Id, activity.Object.Actor) - if err != nil { - return activity, err - } - - activity.Type = "Reject" - if activity.Actor.Id == activity.Object.Actor { - return activity, nil - } - - if alreadyFollow { - query = `delete from follower where id=$1 and follower=$2` - activity.Summary = activity.Object.Actor + " Unfollow " + activity.Actor.Id - - if _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor); err != nil { - return activity, err - } - - activity.Type = "Accept" - return activity, err - } - - query = `insert into follower (id, follower) values ($1, $2)` - activity.Summary = activity.Object.Actor + " Follow " + activity.Actor.Id - - if _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor); err != nil { - return activity, err - } - - activity.Type = "Accept" - return activity, nil -} - func SetActorFollowingDB(activity activitypub.Activity) (activitypub.Activity, error) { var query string alreadyFollowing := false alreadyFollower := false - following, err := GetActorFollowingDB(activity.Object.Actor) + following, err := activitypub.GetActorFollowingDB(activity.Object.Actor) if err != nil { return activity, err } @@ -288,13 +76,13 @@ func SetActorFollowingDB(activity activitypub.Activity) (activitypub.Activity, e if alreadyFollowing && alreadyFollower { query = `delete from following where id=$1 and following=$2` activity.Summary = activity.Object.Actor + " Unfollowing " + activity.Actor.Id - if res, err := IsActorLocal(activity.Actor.Id); err == nil && !res { - go DeleteActorCache(activity.Actor.Id) + if res, err := activitypub.IsActorLocal(activity.Actor.Id); err == nil && !res { + go activitypub.DeleteActorCache(activity.Actor.Id) } else { return activity, err } - if _, err := db.Exec(query, activity.Object.Actor, activity.Actor.Id); err != nil { + if _, err := config.DB.Exec(query, activity.Object.Actor, activity.Actor.Id); err != nil { return activity, err } @@ -306,10 +94,10 @@ func SetActorFollowingDB(activity activitypub.Activity) (activitypub.Activity, e query = `insert into following (id, following) values ($1, $2)` activity.Summary = activity.Object.Actor + " Following " + activity.Actor.Id - if res, err := IsActorLocal(activity.Actor.Id); err == nil && !res { + if res, err := activitypub.IsActorLocal(activity.Actor.Id); err == nil && !res { go WriteActorToCache(activity.Actor.Id) } - if _, err := db.Exec(query, activity.Object.Actor, activity.Actor.Id); err != nil { + if _, err := config.DB.Exec(query, activity.Object.Actor, activity.Actor.Id); err != nil { return activity, err } @@ -321,12 +109,12 @@ func SetActorFollowingDB(activity activitypub.Activity) (activitypub.Activity, e } func AutoFollow(actor string) error { - following, err := GetActorFollowingDB(actor) + following, err := activitypub.GetActorFollowingDB(actor) if err != nil { return err } - follower, err := GetActorFollowDB(actor) + follower, err := activitypub.GetActorFollowDB(actor) if err != nil { return err } @@ -370,7 +158,7 @@ func MakeFollowActivity(actor string, follow string) (activitypub.Activity, erro var obj activitypub.ObjectBase var nactor activitypub.Actor if actor == config.Domain { - nactor, err = GetActorFromDB(actor) + nactor, err = activitypub.GetActorFromDB(actor) } else { nactor, err = webfinger.FingerActor(actor) } @@ -407,7 +195,7 @@ func MakeActivityRequestOutbox(activity activitypub.Activity) error { if activity.Actor.Id == config.Domain { instance = re.ReplaceAllString(config.Domain, "") } else { - _, instance = util.GetActorInstance(activity.Actor.Id) + _, instance = activitypub.GetActorInstance(activity.Actor.Id) } date := time.Now().UTC().Format(time.RFC1123) @@ -416,7 +204,7 @@ func MakeActivityRequestOutbox(activity activitypub.Activity) error { path = re.ReplaceAllString(path, "") sig := fmt.Sprintf("(request-target): %s %s\nhost: %s\ndate: %s", "post", path, instance, date) - encSig, err := ActivitySign(*activity.Actor, sig) + encSig, err := activitypub.ActivitySign(*activity.Actor, sig) if err != nil { return err } @@ -443,7 +231,7 @@ func MakeActivityRequest(activity activitypub.Activity) error { } if actor.Id != "" { - _, instance := util.GetActorInstance(actor.Id) + _, instance := activitypub.GetActorInstance(actor.Id) if actor.Inbox != "" { req, err := http.NewRequest("POST", actor.Inbox, bytes.NewBuffer(j)) @@ -458,7 +246,7 @@ func MakeActivityRequest(activity activitypub.Activity) error { path = re.ReplaceAllString(path, "") sig := fmt.Sprintf("(request-target): %s %s\nhost: %s\ndate: %s", "post", path, instance, date) - encSig, err := ActivitySign(*activity.Actor, sig) + encSig, err := activitypub.ActivitySign(*activity.Actor, sig) if err != nil { return err } diff --git a/db/local.go b/db/local.go deleted file mode 100644 index 7cb46d1..0000000 --- a/db/local.go +++ /dev/null @@ -1,42 +0,0 @@ -package db - -func IsIDLocal(id string) (bool, error) { - activity, err := GetActivityFromDB(id) - return len(activity.OrderedItems) > 0, err -} - -func IsActorLocal(id string) (bool, error) { - actor, err := GetActorFromDB(id) - return actor.Id != "", err -} - -func IsObjectLocal(id string) (bool, error) { - query := `select id from activitystream where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return false, err - } - - var nID string - defer rows.Close() - - rows.Next() - err = rows.Scan(&nID) - return nID != "", err -} - -func IsObjectCached(id string) (bool, error) { - query := `select id from cacheactivitystream where id=$1` - rows, err := db.Query(query, id) - if err != nil { - return false, err - } - - var nID string - defer rows.Close() - - rows.Next() - err = rows.Scan(&nID) - return nID != "", err -} diff --git a/db/outbox.go b/db/outbox.go deleted file mode 100644 index e8189d9..0000000 --- a/db/outbox.go +++ /dev/null @@ -1,116 +0,0 @@ -package db - -import ( - "github.com/FChannel0/FChannel-Server/activitypub" -) - -func GetCollectionFromPath(path string) (activitypub.Collection, error) { - var nColl activitypub.Collection - var result []activitypub.ObjectBase - - query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc` - - rows, err := db.Query(query, path) - if err != nil { - return nColl, err - } - defer rows.Close() - - for rows.Next() { - var actor activitypub.Actor - var post activitypub.ObjectBase - var attachID string - var previewID string - - if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &attachID, &previewID, &actor.Id); err != nil { - return nColl, err - } - - post.Actor = actor.Id - - post.InReplyTo, err = GetInReplyToDB(post) - if err != nil { - return nColl, err - } - - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems, post.Replies.TotalImgs, err = GetObjectRepliesCount(post) - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = post.Replies.TotalItems + postCnt - post.Replies.TotalImgs = post.Replies.TotalImgs + imgCnt - - post.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nColl, err - } - - post.Preview, err = GetObjectPreview(previewID) - if err != nil { - return nColl, err - } - - result = append(result, post) - } - - nColl.AtContext.Context = "https://www.w3.org/ns/activitystreams" - - nColl.OrderedItems = result - - return nColl, nil -} - -func GetObjectFromPath(path string) (activitypub.ObjectBase, error) { - var nObj activitypub.ObjectBase - - query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc` - - rows, err := db.Query(query, path) - if err != nil { - return nObj, err - } - - defer rows.Close() - rows.Next() - var attachID string - var previewID string - - var nActor activitypub.Actor - nObj.Actor = nActor.Id - - if err := rows.Scan(&nObj.Id, &nObj.Name, &nObj.Content, &nObj.Type, &nObj.Published, &nObj.AttributedTo, &attachID, &previewID, &nObj.Actor); err != nil { - return nObj, err - } - - var postCnt int - var imgCnt int - - nObj.Replies, postCnt, imgCnt, err = GetObjectRepliesDB(nObj) - if err != nil { - return nObj, err - } - - nObj.Replies.TotalItems, nObj.Replies.TotalImgs, err = GetObjectRepliesCount(nObj) - if err != nil { - return nObj, err - } - - nObj.Replies.TotalItems = nObj.Replies.TotalItems + postCnt - nObj.Replies.TotalImgs = nObj.Replies.TotalImgs + imgCnt - - nObj.Attachment, err = GetObjectAttachment(attachID) - if err != nil { - return nObj, err - } - - nObj.Preview, err = GetObjectPreview(previewID) - return nObj, err -} @@ -2,270 +2,21 @@ package db import ( "crypto" - crand "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "net/http" - "os" - "regexp" "strings" "time" "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/webfinger" + "github.com/gofiber/fiber/v2" ) -func GetActorPemFromDB(pemID string) (activitypub.PublicKeyPem, error) { - var pem activitypub.PublicKeyPem - - query := `select id, owner, file from publickeypem where id=$1` - - rows, err := db.Query(query, pemID) - if err != nil { - return pem, err - } - - defer rows.Close() - - rows.Next() - rows.Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem) - f, err := os.ReadFile(pem.PublicKeyPem) - if err != nil { - return pem, err - } - - pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) - - return pem, nil -} - -func GetActorPemFileFromDB(pemID string) (string, error) { - query := `select file from publickeypem where id=$1` - rows, err := db.Query(query, pemID) - if err != nil { - return "", err - } - - defer rows.Close() - - var file string - rows.Next() - rows.Scan(&file) - - return file, nil -} - -func CreatePem(actor activitypub.Actor) error { - privatekey, err := rsa.GenerateKey(crand.Reader, 2048) - if err != nil { - return err - } - - privateKeyBytes := x509.MarshalPKCS1PrivateKey(privatekey) - - privateKeyBlock := &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: privateKeyBytes, - } - - privatePem, err := os.Create("./pem/board/" + actor.Name + "-private.pem") - if err != nil { - return err - } - - if err := pem.Encode(privatePem, privateKeyBlock); err != nil { - return err - } - - publickey := &privatekey.PublicKey - publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey) - if err != nil { - return err - } - - publicKeyBlock := &pem.Block{ - Type: "PUBLIC KEY", - Bytes: publicKeyBytes, - } - - publicPem, err := os.Create("./pem/board/" + actor.Name + "-public.pem") - if err != nil { - return err - } - - if err := pem.Encode(publicPem, publicKeyBlock); err != nil { - return err - } - - _, err = os.Stat("./pem/board/" + actor.Name + "-public.pem") - if os.IsNotExist(err) { - return err - } else { - return StorePemToDB(actor) - } - - fmt.Println(`Created PEM keypair for the "` + actor.Name + `" board. Please keep in mind that -the PEM key is crucial in identifying yourself as the legitimate owner of the board, -so DO NOT LOSE IT!!! If you lose it, YOU WILL LOSE ACCESS TO YOUR BOARD!`) - - return nil -} - -func CreatePublicKeyFromPrivate(actor *activitypub.Actor, publicKeyPem string) error { - publicFilename, err := GetActorPemFileFromDB(publicKeyPem) - if err != nil { - return err - } - - privateFilename := strings.ReplaceAll(publicFilename, "public.pem", "private.pem") - if _, err := os.Stat(privateFilename); err == nil { - // Not a lost cause - priv, err := ioutil.ReadFile(privateFilename) - if err != nil { - return err - } - - block, _ := pem.Decode([]byte(priv)) - if block == nil || block.Type != "RSA PRIVATE KEY" { - return errors.New("failed to decode PEM block containing public key") - } - - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return err - } - - publicKeyDer, err := x509.MarshalPKIXPublicKey(&key.PublicKey) - if err != nil { - return err - } - - pubKeyBlock := pem.Block{ - Type: "PUBLIC KEY", - Headers: nil, - Bytes: publicKeyDer, - } - - publicFileWriter, err := os.Create(publicFilename) - if err != nil { - return err - } - - if err := pem.Encode(publicFileWriter, &pubKeyBlock); err != nil { - return err - } - } else { - fmt.Println(`\nUnable to locate private key from public key generation. Now, -this means that you are now missing the proof that you are the -owner of the "` + actor.Name + `" board. If you are the developer, -then your job is just as easy as generating a new keypair, but -if this board is live, then you'll also have to convince the other -owners to switch their public keys for you so that they will start -accepting your posts from your board from this site. Good luck ;)`) - return errors.New("unable to locate private key") - } - return nil -} - -func StorePemToDB(actor activitypub.Actor) error { - query := "select publicKeyPem from actor where id=$1" - rows, err := db.Query(query, actor.Id) - if err != nil { - return err - } - - defer rows.Close() - - var result string - rows.Next() - rows.Scan(&result) - - if result != "" { - return errors.New("already storing public key for actor") - } - - publicKeyPem := actor.Id + "#main-key" - query = "update actor set publicKeyPem=$1 where id=$2" - if _, err := db.Exec(query, publicKeyPem, actor.Id); err != nil { - return err - } - - 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) - return err -} -func ActivitySign(actor activitypub.Actor, signature string) (string, error) { - query := `select file from publicKeyPem where id=$1 ` - - rows, err := db.Query(query, actor.PublicKey.Id) - if err != nil { - return "", err - } - - defer rows.Close() - - var file string - rows.Next() - rows.Scan(&file) - - file = strings.ReplaceAll(file, "public.pem", "private.pem") - _, err = os.Stat(file) - if err != nil { - fmt.Println(`\n Unable to locate private key. Now, -this means that you are now missing the proof that you are the -owner of the "` + actor.Name + `" board. If you are the developer, -then your job is just as easy as generating a new keypair, but -if this board is live, then you'll also have to convince the other -owners to switch their public keys for you so that they will start -accepting your posts from your board from this site. Good luck ;)`) - return "", errors.New("unable to locate private key") - } - - publickey, err := ioutil.ReadFile(file) - if err != nil { - return "", err - } - - block, _ := pem.Decode(publickey) - - pub, _ := x509.ParsePKCS1PrivateKey(block.Bytes) - rng := crand.Reader - hashed := sha256.New() - hashed.Write([]byte(signature)) - cipher, _ := rsa.SignPKCS1v15(rng, pub, crypto.SHA256, hashed.Sum(nil)) - - return base64.StdEncoding.EncodeToString(cipher), nil -} - -func ActivityVerify(actor activitypub.Actor, signature string, verify string) error { - sig, _ := base64.StdEncoding.DecodeString(signature) - - if actor.PublicKey.PublicKeyPem == "" { - _actor, err := webfinger.FingerActor(actor.Id) - if err != nil { - return err - } - actor = _actor - } - - block, _ := pem.Decode([]byte(actor.PublicKey.PublicKeyPem)) - pub, _ := x509.ParsePKIXPublicKey(block.Bytes) - - hashed := sha256.New() - hashed.Write([]byte(verify)) - - return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed.Sum(nil), sig) -} - -func VerifyHeaderSignature(r *http.Request, actor activitypub.Actor) bool { - s := ParseHeaderSignature(r.Header.Get("Signature")) +func VerifyHeaderSignature(ctx *fiber.Ctx, actor activitypub.Actor) bool { + s := activitypub.ParseHeaderSignature(ctx.Get("Signature")) var method string var path string @@ -283,24 +34,24 @@ func VerifyHeaderSignature(r *http.Request, actor activitypub.Actor) bool { switch e { case "(request-target)": - method = strings.ToLower(r.Method) - path = r.URL.Path + method = strings.ToLower(ctx.Method()) + path = ctx.Path() sig += "(request-target): " + method + " " + path + "" + nl break case "host": - host = r.Host + host = ctx.Hostname() sig += "host: " + host + "" + nl break case "date": - date = r.Header.Get("date") + date = ctx.Get("date") sig += "date: " + date + "" + nl break case "digest": - digest = r.Header.Get("digest") + digest = ctx.Get("digest") sig += "digest: " + digest + "" + nl break case "content-length": - contentLength = r.Header.Get("content-length") + contentLength = ctx.Get("content-length") sig += "content-length: " + contentLength + "" + nl break } @@ -323,39 +74,22 @@ func VerifyHeaderSignature(r *http.Request, actor activitypub.Actor) bool { return true } -func ParseHeaderSignature(signature string) Signature { - var nsig Signature - - keyId := regexp.MustCompile(`keyId=`) - headers := regexp.MustCompile(`headers=`) - sig := regexp.MustCompile(`signature=`) - algo := regexp.MustCompile(`algorithm=`) - - signature = strings.ReplaceAll(signature, "\"", "") - parts := strings.Split(signature, ",") - - for _, e := range parts { - if keyId.MatchString(e) { - nsig.KeyId = keyId.ReplaceAllString(e, "") - continue - } +func ActivityVerify(actor activitypub.Actor, signature string, verify string) error { + sig, _ := base64.StdEncoding.DecodeString(signature) - if headers.MatchString(e) { - header := headers.ReplaceAllString(e, "") - nsig.Headers = strings.Split(header, " ") - continue + if actor.PublicKey.PublicKeyPem == "" { + _actor, err := webfinger.FingerActor(actor.Id) + if err != nil { + return err } + actor = _actor + } - if sig.MatchString(e) { - nsig.Signature = sig.ReplaceAllString(e, "") - continue - } + block, _ := pem.Decode([]byte(actor.PublicKey.PublicKeyPem)) + pub, _ := x509.ParsePKIXPublicKey(block.Bytes) - if algo.MatchString(e) { - nsig.Algorithm = algo.ReplaceAllString(e, "") - continue - } - } + hashed := sha256.New() + hashed.Write([]byte(verify)) - return nsig + return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hashed.Sum(nil), sig) } diff --git a/db/report.go b/db/report.go index a64c94f..4120d9a 100644 --- a/db/report.go +++ b/db/report.go @@ -1,5 +1,7 @@ package db +import "github.com/FChannel0/FChannel-Server/config" + type Report struct { ID string Count int @@ -12,65 +14,10 @@ type Removed struct { Board string } -func DeleteReportActivity(id string) error { - query := `delete from reported where id=$1` - - _, err := db.Exec(query, id) - return err -} - -func ReportActivity(id string, reason string) (bool, error) { - if res, err := IsIDLocal(id); err == nil && !res { - // TODO: not local error - return false, nil - } else if err != nil { - return false, err - } - - actor, err := GetActivityFromDB(id) - if err != nil { - return false, err - } - - query := `select count from reported where id=$1` - - rows, err := db.Query(query, id) - if err != nil { - return false, err - } - defer rows.Close() - - var count int - for rows.Next() { - if err := rows.Scan(&count); err != nil { - return false, err - } - } - - 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 { - return false, err - } - } else { - count = count + 1 - query = `update reported set count=$1 where id=$2` - - _, err := db.Exec(query, count, id) - if err != nil { - return false, err - } - } - - return true, nil -} - func CreateLocalDeleteDB(id string, _type string) error { query := `select id from removed where id=$1` - rows, err := db.Query(query, id) + rows, err := config.DB.Query(query, id) if err != nil { return err } @@ -86,14 +33,14 @@ func CreateLocalDeleteDB(id string, _type string) error { if i != "" { query := `update removed set type=$1 where id=$2` - if _, err := db.Exec(query, _type, id); err != nil { + if _, err := config.DB.Exec(query, _type, id); err != nil { return err } } } else { query := `insert into removed (id, type) values ($1, $2)` - if _, err := db.Exec(query, id, _type); err != nil { + if _, err := config.DB.Exec(query, id, _type); err != nil { return err } } @@ -106,7 +53,7 @@ func GetLocalDeleteDB() ([]Removed, error) { query := `select id, type from removed` - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return deleted, err } @@ -129,7 +76,7 @@ func GetLocalDeleteDB() ([]Removed, error) { func CreateLocalReportDB(id string, board string, reason string) error { query := `select id, count from reported where id=$1 and board=$2` - rows, err := db.Query(query, id, board) + rows, err := config.DB.Query(query, id, board) if err != nil { return err } @@ -147,14 +94,14 @@ func CreateLocalReportDB(id string, board string, reason string) error { count = count + 1 query := `update reported set count=$1 where id=$2` - if _, err := db.Exec(query, count, id); err != nil { + if _, err := config.DB.Exec(query, count, id); err != nil { return err } } } else { query := `insert into reported (id, count, board, reason) values ($1, $2, $3, $4)` - if _, err := db.Exec(query, id, 1, board, reason); err != nil { + if _, err := config.DB.Exec(query, id, 1, board, reason); err != nil { return err } } @@ -167,7 +114,7 @@ func GetLocalReportDB(board string) ([]Report, error) { query := `select id, count, reason from reported where board=$1` - rows, err := db.Query(query, board) + rows, err := config.DB.Query(query, board) if err != nil { return reported, err } @@ -189,6 +136,6 @@ func GetLocalReportDB(board string) ([]Report, error) { func CloseLocalReportDB(id string, board string) error { query := `delete from reported where id=$1 and board=$2` - _, err := db.Exec(query, id, board) + _, err := config.DB.Exec(query, id, board) return err } diff --git a/db/verification.go b/db/verification.go index 8be2ffe..c11e392 100644 --- a/db/verification.go +++ b/db/verification.go @@ -8,6 +8,7 @@ import ( "os/exec" "time" + "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/util" _ "github.com/lib/pq" @@ -37,7 +38,7 @@ type Signature struct { func DeleteBoardMod(verify Verify) error { query := `select code from boardaccess where identifier=$1 and board=$1` - rows, err := db.Query(query, verify.Identifier, verify.Board) + rows, err := config.DB.Query(query, verify.Identifier, verify.Board) if err != nil { return err } @@ -51,13 +52,13 @@ func DeleteBoardMod(verify Verify) error { if code != "" { query := `delete from crossverification where code=$1` - if _, err := db.Exec(query, code); err != nil { + if _, err := config.DB.Exec(query, code); err != nil { return err } query = `delete from boardaccess where identifier=$1 and board=$2` - if _, err := db.Exec(query, verify.Identifier, verify.Board); err != nil { + if _, err := config.DB.Exec(query, verify.Identifier, verify.Board); err != nil { return err } } @@ -70,7 +71,7 @@ func GetBoardMod(identifier string) (Verify, error) { query := `select code, board, type, identifier from boardaccess where identifier=$1` - rows, err := db.Query(query, identifier) + rows, err := config.DB.Query(query, identifier) if err != nil { return nVerify, err @@ -89,7 +90,7 @@ func CreateBoardMod(verify Verify) error { query := `select code from verification where identifier=$1 and type=$2` - rows, err := db.Query(query, verify.Board, verify.Type) + rows, err := config.DB.Query(query, verify.Board, verify.Type) if err != nil { return err } @@ -105,7 +106,7 @@ func CreateBoardMod(verify Verify) error { query := `select identifier from boardaccess where identifier=$1 and board=$2` - rows, err := db.Query(query, verify.Identifier, verify.Board) + rows, err := config.DB.Query(query, verify.Identifier, verify.Board) if err != nil { return err } @@ -120,13 +121,13 @@ func CreateBoardMod(verify Verify) error { query := `insert into crossverification (verificationcode, code) values ($1, $2)` - if _, err := db.Exec(query, code, pass); err != nil { + if _, err := config.DB.Exec(query, code, pass); err != nil { return err } query = `insert into boardaccess (identifier, code, board, type) values ($1, $2, $3, $4)` - if _, err = db.Exec(query, verify.Identifier, pass, verify.Board, verify.Type); err != nil { + if _, err = config.DB.Exec(query, verify.Identifier, pass, verify.Board, verify.Type); err != nil { return err } @@ -140,7 +141,7 @@ func CreateBoardMod(verify Verify) error { func CreateVerification(verify Verify) error { query := `insert into verification (type, identifier, code, created) values ($1, $2, $3, $4)` - _, err := db.Exec(query, verify.Type, verify.Identifier, verify.Code, time.Now().UTC().Format(time.RFC3339)) + _, err := config.DB.Exec(query, verify.Type, verify.Identifier, verify.Code, time.Now().UTC().Format(time.RFC3339)) return err } @@ -151,7 +152,7 @@ func GetVerificationByEmail(email string) (Verify, error) { query := `select type, identifier, code, board from boardaccess where identifier=$1` - rows, err := db.Query(query, email) + rows, err := config.DB.Query(query, email) if err != nil { return verify, err } @@ -174,7 +175,7 @@ func GetVerificationByCode(code string) (Verify, error) { query := `select type, identifier, code, board from boardaccess where code=$1` - rows, err := db.Query(query, code) + rows, err := config.DB.Query(query, code) if err != nil { return verify, err } @@ -195,7 +196,7 @@ func GetVerificationCode(verify Verify) (Verify, error) { query := `select type, identifier, code, board from boardaccess where identifier=$1 and board=$2` - rows, err := db.Query(query, verify.Identifier, verify.Board) + rows, err := config.DB.Query(query, verify.Identifier, verify.Board) if err != nil { return verify, err } @@ -217,11 +218,11 @@ func VerifyCooldownCurrent(auth string) (VerifyCooldown, error) { query := `select identifier, code, time from verificationcooldown where code=$1` - rows, err := db.Query(query, auth) + rows, err := config.DB.Query(query, auth) if err != nil { query := `select identifier, code, time from verificationcooldown where identifier=$1` - rows, err := db.Query(query, auth) + rows, err := config.DB.Query(query, auth) if err != nil { return current, err @@ -250,14 +251,14 @@ func VerifyCooldownCurrent(auth string) (VerifyCooldown, error) { func VerifyCooldownAdd(verify Verify) error { query := `insert into verficationcooldown (identifier, code) values ($1, $2)` - _, err := db.Exec(query, verify.Identifier, verify.Code) + _, err := config.DB.Exec(query, verify.Identifier, verify.Code) return err } func VerficationCooldown() error { query := `select identifier, code, time from verificationcooldown` - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return err } @@ -275,7 +276,7 @@ func VerficationCooldown() error { query = `update set time=$1 where identifier=$2` - if _, err := db.Exec(query, nTime, verify.Identifier); err != nil { + if _, err := config.DB.Exec(query, nTime, verify.Identifier); err != nil { return err } @@ -288,7 +289,7 @@ func VerficationCooldown() error { func VerficationCooldownRemove() error { query := `delete from verificationcooldown where time < 1` - _, err := db.Exec(query) + _, err := config.DB.Exec(query) return err } @@ -431,7 +432,7 @@ func CreateBoardAccess(verify Verify) error { if !hasAccess { query := `insert into boardaccess (identifier, board) values($1, $2)` - _, err := db.Exec(query, verify.Identifier, verify.Board) + _, err := config.DB.Exec(query, verify.Identifier, verify.Board) return err } @@ -441,7 +442,7 @@ func CreateBoardAccess(verify Verify) error { func HasBoardAccess(verify Verify) (bool, error) { query := `select count(*) from boardaccess where identifier=$1 and board=$2` - rows, err := db.Query(query, verify.Identifier, verify.Board) + rows, err := config.DB.Query(query, verify.Identifier, verify.Board) if err != nil { return false, err } @@ -461,7 +462,7 @@ func HasBoardAccess(verify Verify) (bool, error) { } func BoardHasAuthType(board string, auth string) (bool, error) { - authTypes, err := GetActorAuth(board) + authTypes, err := activitypub.GetActorAuth(board) if err != nil { return false, err } @@ -2,7 +2,6 @@ package main import ( "encoding/json" - "errors" "fmt" "github.com/FChannel0/FChannel-Server/activitypub" @@ -23,7 +22,6 @@ import ( "io/ioutil" // "log" "math/rand" - "mime/multipart" "net/http" "os" "os/exec" @@ -60,7 +58,8 @@ func main() { config.Key = util.CreateKey(32) - db.FollowingBoards, err = db.GetActorFollowingDB(config.Domain) + webfinger.FollowingBoards, err = activitypub.GetActorFollowingDB(config.Domain) + if err != nil { panic(err) } @@ -69,7 +68,8 @@ func main() { go db.CheckInactive() - db.Boards, err = db.GetBoardCollection() + webfinger.Boards, err = webfinger.GetBoardCollection() + if err != nil { panic(err) } @@ -77,7 +77,7 @@ func main() { // root actor is used to follow remote feeds that are not local //name, prefname, summary, auth requirements, restricted if config.InstanceName != "" { - if _, err = db.CreateNewBoardDB(*CreateNewActor("", config.InstanceName, config.InstanceSummary, authReq, false)); err != nil { + if _, err = db.CreateNewBoardDB(*activitypub.CreateNewActor("", config.InstanceName, config.InstanceSummary, authReq, false)); err != nil { //panic(err) } @@ -189,7 +189,7 @@ func main() { actorDomain[0] = "/" + actorDomain[0] } - if res, err := db.IsActorLocal(config.TP + "" + actorDomain[1] + "" + actorDomain[0]); err == nil && !res { + if res, err := activitypub.IsActorLocal(config.TP + "" + actorDomain[1] + "" + actorDomain[0]); err == nil && !res { c.Status(fiber.StatusBadRequest) return c.Send([]byte("actor not local")) } else if err != nil { @@ -268,34 +268,8 @@ func GetContentType(location string) string { } } -func CreateNewActor(board string, prefName string, summary string, authReq []string, restricted bool) *activitypub.Actor { - actor := new(activitypub.Actor) - - var path string - if board == "" { - path = config.Domain - actor.Name = "main" - } else { - path = config.Domain + "/" + board - actor.Name = board - } - - actor.Type = "Group" - 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 GetActorPost(w http.ResponseWriter, path string) error { - collection, err := db.GetCollectionFromPath(config.Domain + "" + path) + collection, err := activitypub.GetCollectionFromPath(config.Domain + "" + path) if err != nil { return err } @@ -314,16 +288,6 @@ func GetActorPost(w http.ResponseWriter, path string) error { return nil } -func CreateObject(objType string) activitypub.ObjectBase { - var nObj activitypub.ObjectBase - - nObj.Type = objType - nObj.Published = time.Now().UTC() - nObj.Updated = time.Now().UTC() - - return nObj -} - func AddFollowersToActivity(activity activitypub.Activity) (activitypub.Activity, error) { activity.To = append(activity.To, activity.Actor.Id) @@ -387,101 +351,6 @@ func CreateActivity(activityType string, obj activitypub.ObjectBase) (activitypu return newActivity, nil } -func ProcessActivity(activity activitypub.Activity) error { - activityType := activity.Type - - if activityType == "Create" { - for _, e := range activity.To { - if res, err := db.GetActorFromDB(e); err == nil && res.Id != "" { - fmt.Println("actor is in the database") - } else if err != nil { - return err - } else { - fmt.Println("actor is NOT in the database") - } - } - } else if activityType == "Follow" { - // TODO: okay? - return errors.New("not implemented") - } else if activityType == "Delete" { - return errors.New("not implemented") - } - - return nil -} - -func CreatePreviewObject(obj activitypub.ObjectBase) *activitypub.NestedObjectBase { - re := regexp.MustCompile(`/.+$`) - - mimetype := re.ReplaceAllString(obj.MediaType, "") - - var nPreview activitypub.NestedObjectBase - - if mimetype != "image" { - return &nPreview - } - - re = regexp.MustCompile(`.+/`) - - file := re.ReplaceAllString(obj.MediaType, "") - - href := util.GetUniqueFilename(file) - - nPreview.Type = "Preview" - nPreview.Name = obj.Name - nPreview.Href = config.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>", "-strip", "."+href) - - if err := cmd.Run(); err != nil { - // TODO: previously we would call CheckError here - var preview activitypub.NestedObjectBase - return &preview - } - - return &nPreview -} - -func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]activitypub.ObjectBase, *os.File, error) { - contentType, err := GetFileContentType(file) - if err != nil { - return nil, nil, err - } - - filename := header.Filename - size := header.Size - - re := regexp.MustCompile(`.+/`) - - fileType := re.ReplaceAllString(contentType, "") - - tempFile, err := ioutil.TempFile("./public", "*."+fileType) - if err != nil { - return nil, nil, err - } - - var nAttachment []activitypub.ObjectBase - var image activitypub.ObjectBase - - image.Type = "Attachment" - image.Name = filename - image.Href = config.Domain + "/" + tempFile.Name() - image.MediaType = contentType - image.Size = size - image.Published = time.Now().UTC() - - nAttachment = append(nAttachment, image) - - return nAttachment, tempFile, nil -} - func ParseCommentForReplies(comment string, op string) ([]activitypub.ObjectBase, error) { re := regexp.MustCompile(`(>>(https?://[A-Za-z0-9_.:\-~]+\/[A-Za-z0-9_.\-~]+\/)(f[A-Za-z0-9_.\-~]+-)?([A-Za-z0-9_.\-~]+)?#?([A-Za-z0-9_.\-~]+)?)`) @@ -528,36 +397,6 @@ func IsValidActor(id string) (activitypub.Actor, bool, error) { return actor, actor.Id != "", err } -func IsActivityLocal(activity activitypub.Activity) (bool, error) { - for _, e := range activity.To { - if res, err := db.GetActorFromDB(e); err == nil && res.Id != "" { - return true, nil - } else if err != nil { - return false, err - } - } - - for _, e := range activity.Cc { - if res, err := db.GetActorFromDB(e); err == nil && res.Id != "" { - return true, nil - } else if err != nil { - return false, err - } - } - - if res, err := db.GetActorFromDB(activity.Actor.Id); err == nil && activity.Actor != nil && res.Id != "" { - return true, nil - } else if err != nil { - return false, err - } - - return false, nil -} - -func GetObjectFromActivity(activity activitypub.Activity) activitypub.ObjectBase { - return *activity.Object -} - func MakeCaptchas(total int) error { dbtotal, err := db.GetCaptchaTotal() if err != nil { @@ -575,21 +414,6 @@ func MakeCaptchas(total int) error { return nil } -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 SupportedMIMEType(mime string) bool { for _, e := range config.SupportedFiles { if e == mime { @@ -623,12 +447,12 @@ func GetActorReported(w http.ResponseWriter, r *http.Request, id string) error { following.AtContext.Context = "https://www.w3.org/ns/activitystreams" following.Type = "Collection" - following.TotalItems, err = db.GetActorReportedTotal(id) + following.TotalItems, err = activitypub.GetActorReportedTotal(id) if err != nil { return err } - following.Items, err = db.GetActorReportedDB(id) + following.Items, err = activitypub.GetActorReportedDB(id) if err != nil { return err } @@ -644,36 +468,6 @@ func GetActorReported(w http.ResponseWriter, r *http.Request, id string) error { return err } -func GetCollectionFromID(id string) (activitypub.Collection, error) { - var nColl activitypub.Collection - - req, err := http.NewRequest("GET", id, nil) - if err != nil { - return nColl, err - } - - req.Header.Set("Accept", config.ActivityStreams) - - resp, err := util.RouteProxy(req) - if err != nil { - return nColl, err - } - - if resp.StatusCode == 200 { - defer resp.Body.Close() - - body, _ := ioutil.ReadAll(resp.Body) - - if len(body) > 0 { - if err := json.Unmarshal(body, &nColl); err != nil { - return nColl, err - } - } - } - - return nColl, nil -} - func PrintAdminAuth() error { identifier, code, err := db.GetAdminAuth() if err != nil { @@ -695,7 +489,7 @@ func DeleteObjectRequest(id string) error { return err } - obj, err := db.GetObjectFromPath(id) + obj, err := activitypub.GetObjectFromPath(id) if err != nil { return err } @@ -706,7 +500,7 @@ func DeleteObjectRequest(id string) error { } activity.Actor = &actor - followers, err := db.GetActorFollowDB(obj.Actor) + followers, err := activitypub.GetActorFollowDB(obj.Actor) if err != nil { return err } @@ -715,7 +509,7 @@ func DeleteObjectRequest(id string) error { activity.To = append(activity.To, e.Id) } - following, err := db.GetActorFollowingDB(obj.Actor) + following, err := activitypub.GetActorFollowingDB(obj.Actor) if err != nil { return err } @@ -737,7 +531,7 @@ func DeleteObjectAndRepliesRequest(id string) error { return err } - obj, err := db.GetObjectByIDFromDB(id) + obj, err := activitypub.GetObjectByIDFromDB(id) if err != nil { return err } @@ -746,7 +540,7 @@ func DeleteObjectAndRepliesRequest(id string) error { activity.Object = &obj.OrderedItems[0] - followers, err := db.GetActorFollowDB(obj.OrderedItems[0].Actor) + followers, err := activitypub.GetActorFollowDB(obj.OrderedItems[0].Actor) if err != nil { return err } @@ -754,7 +548,7 @@ func DeleteObjectAndRepliesRequest(id string) error { activity.To = append(activity.To, e.Id) } - following, err := db.GetActorFollowingDB(obj.OrderedItems[0].Actor) + following, err := activitypub.GetActorFollowingDB(obj.OrderedItems[0].Actor) if err != nil { return err } @@ -767,7 +561,7 @@ func DeleteObjectAndRepliesRequest(id string) error { } func ResizeAttachmentToPreview() error { - return db.GetObjectsWithoutPreviewsCallback(func(id, href, mediatype, name string, size int, published time.Time) error { + return activitypub.GetObjectsWithoutPreviewsCallback(func(id, href, mediatype, name string, size int, published time.Time) error { re := regexp.MustCompile(`^\w+`) _type := re.FindString(mediatype) @@ -786,7 +580,7 @@ func ResizeAttachmentToPreview() error { actor := re.ReplaceAllString(id, "") nPreview.Type = "Preview" - uid, err := db.CreateUniqueID(actor) + uid, err := util.CreateUniqueID(actor) if err != nil { return err } @@ -808,10 +602,10 @@ func ResizeAttachmentToPreview() error { if err := cmd.Run(); err == nil { fmt.Println(objFile + " -> " + nHref) - if err := db.WritePreviewToDB(nPreview); err != nil { + if err := activitypub.WritePreviewToDB(nPreview); err != nil { return err } - if err := db.UpdateObjectWithPreview(id, nPreview.Id); err != nil { + if err := activitypub.UpdateObjectWithPreview(id, nPreview.Id); err != nil { return err } } else { @@ -849,39 +643,6 @@ func ParseCommentForReply(comment string) (string, error) { return "", nil } -func GetActorCollectionReq(r *http.Request, collection string) (activitypub.Collection, error) { - var nCollection activitypub.Collection - - req, err := http.NewRequest("GET", collection, nil) - if err != nil { - return nCollection, err - } - - // TODO: rewrite this for fiber - pass := "FIXME" - //_, pass := GetPasswordFromSession(r) - - req.Header.Set("Accept", config.ActivityStreams) - - req.Header.Set("Authorization", "Basic "+pass) - - resp, err := util.RouteProxy(req) - if err != nil { - return nCollection, err - } - defer resp.Body.Close() - - if resp.StatusCode == 200 { - body, _ := ioutil.ReadAll(resp.Body) - - if err := json.Unmarshal(body, &nCollection); err != nil { - return nCollection, err - } - } - - return nCollection, nil -} - func CreatedNeededDirectories() { if _, err := os.Stat("./public"); os.IsNotExist(err) { os.Mkdir("./public", 0755) @@ -905,7 +666,7 @@ func AddInstanceToIndex(actor string) error { // also while i'm here // TODO: maybe allow different indexes? - followers, err := GetCollectionFromID("https://fchan.xyz/followers") + followers, err := activitypub.GetCollectionFromID("https://fchan.xyz/followers") if err != nil { return err } @@ -950,7 +711,7 @@ func AddInstanceToIndexDB(actor string) error { } // TODO: maybe allow different indexes? - followers, err := GetCollectionFromID("https://fchan.xyz/followers") + followers, err := activitypub.GetCollectionFromID("https://fchan.xyz/followers") if err != nil { return err } @@ -963,7 +724,7 @@ func AddInstanceToIndexDB(actor string) error { } if !alreadyIndex { - return db.AddFollower("https://fchan.xyz", nActor.Id) + return activitypub.AddFollower("https://fchan.xyz", nActor.Id) } return nil diff --git a/outboxGet.go b/outboxGet.go index 6e7bd47..27e9cc4 100644 --- a/outboxGet.go +++ b/outboxGet.go @@ -7,19 +7,19 @@ import ( "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" - "github.com/FChannel0/FChannel-Server/db" + "github.com/FChannel0/FChannel-Server/webfinger" _ "github.com/lib/pq" ) func GetActorOutbox(w http.ResponseWriter, r *http.Request) error { - actor, err := db.GetActorFromPath(r.URL.Path, "/") + actor, err := webfinger.GetActorFromPath(r.URL.Path, "/") if err != nil { return err } var collection activitypub.Collection - c, err := db.GetActorObjectCollectionFromDB(actor.Id) + c, err := activitypub.GetActorObjectCollectionFromDB(actor.Id) if err != nil { return err } @@ -28,12 +28,12 @@ func GetActorOutbox(w http.ResponseWriter, r *http.Request) error { collection.AtContext.Context = "https://www.w3.org/ns/activitystreams" collection.Actor = &actor - collection.TotalItems, err = db.GetObjectPostsTotalDB(actor) + collection.TotalItems, err = activitypub.GetObjectPostsTotalDB(actor) if err != nil { return err } - collection.TotalImgs, err = db.GetObjectImgsTotalDB(actor) + collection.TotalImgs, err = activitypub.GetObjectImgsTotalDB(actor) if err != nil { return err } diff --git a/outboxPost.go b/outboxPost.go index 3e1e392..d0aa84f 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -14,67 +14,67 @@ import ( "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" + "github.com/FChannel0/FChannel-Server/post" "github.com/FChannel0/FChannel-Server/util" "github.com/FChannel0/FChannel-Server/webfinger" + "github.com/gofiber/fiber/v2" _ "github.com/lib/pq" ) -func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { +func ParseOutboxRequest(ctx *fiber.Ctx) error { //var activity activitypub.Activity - actor, err := db.GetActorFromPath(r.URL.Path, "/") + actor, err := webfinger.GetActorFromPath(ctx.Path(), "/") if err != nil { return err } - contentType := GetContentType(r.Header.Get("content-type")) - - defer r.Body.Close() + contentType := GetContentType(ctx.Get("content-type")) if contentType == "multipart/form-data" || contentType == "application/x-www-form-urlencoded" { - r.ParseMultipartForm(5 << 20) hasCaptcha, err := db.BoardHasAuthType(actor.Name, "captcha") if err != nil { return err } - valid, err := CheckCaptcha(r.FormValue("captcha")) + valid, err := CheckCaptcha(ctx.FormValue("captcha")) if err == nil && hasCaptcha && valid { - f, header, _ := r.FormFile("file") + header, _ := ctx.FormFile("file") if header != nil { + f, _ := header.Open() defer f.Close() if header.Size > (7 << 20) { - w.WriteHeader(http.StatusRequestEntityTooLarge) - _, err := w.Write([]byte("7MB max file size")) - return err + return ctx.Render("403", fiber.Map{ + "message": "7MB max file size", + }) } else if res, err := IsMediaBanned(f); err == nil && res { + //Todo add logging fmt.Println("media banned") - http.Redirect(w, r, config.Domain, http.StatusSeeOther) - return nil + return ctx.Redirect("/", 301) } else if err != nil { return err } - contentType, _ := GetFileContentType(f) + contentType, _ := post.GetFileContentType(f) if !SupportedMIMEType(contentType) { - w.WriteHeader(http.StatusNotAcceptable) - _, err := w.Write([]byte("file type not supported")) - return err + return ctx.Render("403", fiber.Map{ + "message": "file type not supported", + }) } } - var nObj = CreateObject("Note") - nObj, err := ObjectFromForm(r, nObj) + var nObj = activitypub.CreateObject("Note") + nObj, err := ObjectFromForm(ctx, nObj) if err != nil { return err } nObj.Actor = config.Domain + "/" + actor.Name - nObj, err = db.WriteObjectToDB(nObj) + nObj, err = activitypub.WriteObjectToDB(nObj) if err != nil { return err } @@ -107,31 +107,31 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { } } - w.WriteHeader(http.StatusOK) - _, err = w.Write([]byte(id)) + ctx.Response().Header.Add("status", "200") + _, err = ctx.Write([]byte(id)) return err } - w.WriteHeader(http.StatusForbidden) - _, err = w.Write([]byte("captcha could not auth")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("captcha could not auth")) return err } else { - activity, err := GetActivityFromJson(r) + activity, err := activitypub.GetActivityFromJson(ctx) if err != nil { return err } - if res, err := IsActivityLocal(activity); err == nil && res { - if res := db.VerifyHeaderSignature(r, *activity.Actor); err == nil && !res { - w.WriteHeader(http.StatusBadRequest) - _, err = w.Write([]byte("")) + if res, err := activitypub.IsActivityLocal(activity); err == nil && res { + if res := db.VerifyHeaderSignature(ctx, *activity.Actor); err == nil && !res { + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("")) return err } switch activity.Type { case "Create": - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("")) break case "Follow": @@ -153,12 +153,12 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { } } - db.FollowingBoards, err = db.GetActorFollowingDB(config.Domain) + webfinger.FollowingBoards, err = activitypub.GetActorFollowingDB(config.Domain) if err != nil { return err } - db.Boards, err = db.GetBoardCollection() + webfinger.Boards, err = webfinger.GetBoardCollection() if err != nil { return err } @@ -166,13 +166,13 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { case "Delete": fmt.Println("This is a delete") - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("could not process activity")) break case "Note": - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("could not process activity")) break case "New": @@ -181,7 +181,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { summary := activity.Object.Summary restricted := activity.Object.Sensitive - actor, err := db.CreateNewBoardDB(*CreateNewActor(name, prefname, summary, authReq, restricted)) + actor, err := db.CreateNewBoardDB(*activitypub.CreateNewActor(name, prefname, summary, authReq, restricted)) if err != nil { return err } @@ -192,7 +192,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { var removed bool = false item.Id = actor.Id - for _, e := range db.FollowingBoards { + for _, e := range webfinger.FollowingBoards { if e.Id != item.Id { board = append(board, e) } else { @@ -204,25 +204,25 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { board = append(board, item) } - db.FollowingBoards = board - db.Boards, err = db.GetBoardCollection() + webfinger.FollowingBoards = board + webfinger.Boards, err = webfinger.GetBoardCollection() return err } - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("")) break default: - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("could not process activity")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("could not process activity")) } } else if err != nil { return err } else { fmt.Println("is NOT activity") - w.WriteHeader(http.StatusBadRequest) - _, err = w.Write([]byte("could not process activity")) + ctx.Response().Header.Add("status", "403") + _, err = ctx.Write([]byte("could not process activity")) return err } } @@ -230,182 +230,16 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request) error { return nil } -func ObjectFromJson(r *http.Request, obj activitypub.ObjectBase) (activitypub.ObjectBase, error) { - body, _ := ioutil.ReadAll(r.Body) - - var respActivity activitypub.ActivityRaw - - err := json.Unmarshal(body, &respActivity) - if err != nil { - return obj, err - } - - res, err := HasContextFromJson(respActivity.AtContextRaw.Context) - - if err == nil && res { - var jObj activitypub.ObjectBase - jObj, err = GetObjectFromJson(respActivity.ObjectRaw) - if err != nil { - return obj, err - } - - jObj.To, err = GetToFromJson(respActivity.ToRaw) - if err != nil { - return obj, err - } - - jObj.Cc, err = GetToFromJson(respActivity.CcRaw) - } - - return obj, err -} - -func GetObjectFromJson(obj []byte) (activitypub.ObjectBase, error) { - var generic interface{} - var nObj activitypub.ObjectBase - - if err := json.Unmarshal(obj, &generic); err != nil { - return activitypub.ObjectBase{}, err - } - - if generic != nil { - switch generic.(type) { - case []interface{}: - var lObj activitypub.ObjectBase - var arrContext activitypub.ObjectArray - - if err := json.Unmarshal(obj, &arrContext.Object); err != nil { - return nObj, err - } - - if len(arrContext.Object) > 0 { - lObj = arrContext.Object[0] - } - nObj = lObj - break - - case map[string]interface{}: - var arrContext activitypub.Object - - if err := json.Unmarshal(obj, &arrContext.Object); err != nil { - return nObj, err - } - - nObj = *arrContext.Object - break - - case string: - var lObj activitypub.ObjectBase - var arrContext activitypub.ObjectString - - if err := json.Unmarshal(obj, &arrContext.Object); err != nil { - return nObj, err - } - - lObj.Id = arrContext.Object - nObj = lObj - break - } - } - - return nObj, nil -} - -func GetActorFromJson(actor []byte) (activitypub.Actor, error) { - var generic interface{} - var nActor activitypub.Actor - err := json.Unmarshal(actor, &generic) - if err != nil { - return nActor, err - } - - if generic != nil { - switch generic.(type) { - case map[string]interface{}: - err = json.Unmarshal(actor, &nActor) - break - - case string: - var str string - err = json.Unmarshal(actor, &str) - nActor.Id = str - break - } - - return nActor, err - } - - return nActor, nil -} - -func GetToFromJson(to []byte) ([]string, error) { - var generic interface{} - - err := json.Unmarshal(to, &generic) - if err != nil { - return nil, err - } - - if generic != nil { - var nStr []string - switch generic.(type) { - case []interface{}: - err = json.Unmarshal(to, &nStr) - break - case string: - var str string - err = json.Unmarshal(to, &str) - nStr = append(nStr, str) - break - } - return nStr, err - } - - return nil, nil -} - -func HasContextFromJson(context []byte) (bool, error) { - var generic interface{} - - err := json.Unmarshal(context, &generic) - if err != nil { - return false, err - } - - hasContext := false - - switch generic.(type) { - case []interface{}: - var arrContext activitypub.AtContextArray - err = json.Unmarshal(context, &arrContext.Context) - if len(arrContext.Context) > 0 { - if arrContext.Context[0] == "https://www.w3.org/ns/activitystreams" { - hasContext = true - } - } - break - - case string: - var arrContext activitypub.AtContextString - err = json.Unmarshal(context, &arrContext.Context) - if arrContext.Context == "https://www.w3.org/ns/activitystreams" { - hasContext = true - } - break - } - - return hasContext, err -} - -func ObjectFromForm(r *http.Request, obj activitypub.ObjectBase) (activitypub.ObjectBase, error) { - file, header, _ := r.FormFile("file") +func ObjectFromForm(ctx *fiber.Ctx, obj activitypub.ObjectBase) (activitypub.ObjectBase, error) { + header, _ := ctx.FormFile("file") + file, _ := header.Open() var err error if file != nil { defer file.Close() var tempFile = new(os.File) - obj.Attachment, tempFile, err = CreateAttachmentObject(file, header) + obj.Attachment, tempFile, err = activitypub.CreateAttachmentObject(file, header) if err != nil { return obj, err } @@ -427,19 +261,19 @@ func ObjectFromForm(r *http.Request, obj activitypub.ObjectBase) (activitypub.Ob } } - obj.Preview = CreatePreviewObject(obj.Attachment[0]) + obj.Preview = activitypub.CreatePreviewObject(obj.Attachment[0]) } - obj.AttributedTo = util.EscapeString(r.FormValue("name")) - obj.TripCode = util.EscapeString(r.FormValue("tripcode")) - obj.Name = util.EscapeString(r.FormValue("subject")) - obj.Content = util.EscapeString(r.FormValue("comment")) - obj.Sensitive = (r.FormValue("sensitive") != "") + obj.AttributedTo = util.EscapeString(ctx.FormValue("name")) + obj.TripCode = util.EscapeString(ctx.FormValue("tripcode")) + obj.Name = util.EscapeString(ctx.FormValue("subject")) + obj.Content = util.EscapeString(ctx.FormValue("comment")) + obj.Sensitive = (ctx.FormValue("sensitive") != "") - obj = ParseOptions(r, obj) + obj = ParseOptions(ctx, obj) var originalPost activitypub.ObjectBase - originalPost.Id = util.EscapeString(r.FormValue("inReplyTo")) + originalPost.Id = util.EscapeString(ctx.FormValue("inReplyTo")) obj.InReplyTo = append(obj.InReplyTo, originalPost) @@ -450,7 +284,7 @@ func ObjectFromForm(r *http.Request, obj activitypub.ObjectBase) (activitypub.Ob } if originalPost.Id != "" { - if res, err := IsActivityLocal(activity); err == nil && !res { + if res, err := activitypub.IsActivityLocal(activity); err == nil && !res { actor, err := webfinger.FingerActor(originalPost.Id) if err != nil { return obj, err @@ -464,7 +298,7 @@ func ObjectFromForm(r *http.Request, obj activitypub.ObjectBase) (activitypub.Ob } } - replyingTo, err := ParseCommentForReplies(r.FormValue("comment"), originalPost.Id) + replyingTo, err := ParseCommentForReplies(ctx.FormValue("comment"), originalPost.Id) if err != nil { return obj, err } @@ -486,7 +320,7 @@ func ObjectFromForm(r *http.Request, obj activitypub.ObjectBase) (activitypub.Ob activity.To = append(activity.To, e.Id) - if res, err := IsActivityLocal(activity); err == nil && !res { + if res, err := activitypub.IsActivityLocal(activity); err == nil && !res { actor, err := webfinger.FingerActor(e.Id) if err != nil { return obj, err @@ -504,8 +338,8 @@ func ObjectFromForm(r *http.Request, obj activitypub.ObjectBase) (activitypub.Ob return obj, nil } -func ParseOptions(r *http.Request, obj activitypub.ObjectBase) activitypub.ObjectBase { - options := util.EscapeString(r.FormValue("options")) +func ParseOptions(ctx *fiber.Ctx, obj activitypub.ObjectBase) activitypub.ObjectBase { + options := util.EscapeString(ctx.FormValue("options")) if options != "" { option := strings.Split(options, ";") email := regexp.MustCompile(".+@.+\\..+") @@ -536,74 +370,6 @@ func ParseOptions(r *http.Request, obj activitypub.ObjectBase) activitypub.Objec return obj } -func GetActivityFromJson(r *http.Request) (activitypub.Activity, error) { - body, _ := ioutil.ReadAll(r.Body) - - var respActivity activitypub.ActivityRaw - var nActivity activitypub.Activity - var nType string - - if err := json.Unmarshal(body, &respActivity); err != nil { - return nActivity, err - } - - if res, err := HasContextFromJson(respActivity.AtContextRaw.Context); err == nil && res { - var jObj activitypub.ObjectBase - - if respActivity.Type == "Note" { - jObj, err = GetObjectFromJson(body) - if err != nil { - return nActivity, err - } - - nType = "Create" - } else { - jObj, err = GetObjectFromJson(respActivity.ObjectRaw) - if err != nil { - return nActivity, err - } - - nType = respActivity.Type - } - - actor, err := GetActorFromJson(respActivity.ActorRaw) - if err != nil { - return nActivity, err - } - - to, err := GetToFromJson(respActivity.ToRaw) - if err != nil { - return nActivity, err - } - - cc, err := GetToFromJson(respActivity.CcRaw) - if err != nil { - return nActivity, err - } - - nActivity.AtContext.Context = "https://www.w3.org/ns/activitystreams" - nActivity.Type = nType - nActivity.Actor = &actor - nActivity.Published = respActivity.Published - nActivity.Auth = respActivity.Auth - - if len(to) > 0 { - nActivity.To = to - } - - if len(cc) > 0 { - nActivity.Cc = cc - } - - nActivity.Name = respActivity.Name - nActivity.Object = &jObj - } else if err != nil { - return nActivity, err - } - - return nActivity, nil -} - func CheckCaptcha(captcha string) (bool, error) { parts := strings.Split(captcha, ":") @@ -633,8 +399,8 @@ func CheckCaptcha(captcha string) (bool, error) { return code == strings.ToUpper(parts[1]), nil } -func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { - activity, err := GetActivityFromJson(r) +func ParseInboxRequest(ctx *fiber.Ctx) error { + activity, err := activitypub.GetActivityFromJson(ctx) if err != nil { return err } @@ -648,8 +414,8 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { activity.Actor = &nActor } - if !db.VerifyHeaderSignature(r, *activity.Actor) { - response := db.RejectActivity(activity) + if !db.VerifyHeaderSignature(ctx, *activity.Actor) { + response := activitypub.RejectActivity(activity) return db.MakeActivityRequest(response) } @@ -658,9 +424,9 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { case "Create": for _, e := range activity.To { - if res, err := db.IsActorLocal(e); err == nil && res { - if res, err := db.IsActorLocal(activity.Actor.Id); err == nil && res { - col, err := GetCollectionFromID(activity.Object.Id) + if res, err := activitypub.IsActorLocal(e); err == nil && res { + if res, err := activitypub.IsActorLocal(activity.Actor.Id); err == nil && res { + col, err := activitypub.GetCollectionFromID(activity.Object.Id) if err != nil { return err } @@ -669,11 +435,11 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { break } - if _, err := db.WriteObjectToCache(*activity.Object); err != nil { + if _, err := activitypub.WriteObjectToCache(*activity.Object); err != nil { return err } - actor, err := db.GetActorFromDB(e) + actor, err := activitypub.GetActorFromDB(e) if err != nil { return err } @@ -695,7 +461,7 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { case "Delete": for _, e := range activity.To { - actor, err := db.GetActorFromDB(e) + actor, err := activitypub.GetActorFromDB(e) if err != nil { return err } @@ -703,13 +469,13 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { if actor.Id != "" && actor.Id != config.Domain { if activity.Object.Replies != nil { for _, k := range activity.Object.Replies.OrderedItems { - if err := db.TombstoneObject(k.Id); err != nil { + if err := activitypub.TombstoneObject(k.Id); err != nil { return err } } } - if err := db.TombstoneObject(activity.Object.Id); err != nil { + if err := activitypub.TombstoneObject(activity.Object.Id); err != nil { return err } if err := db.UnArchiveLast(actor.Id); err != nil { @@ -722,9 +488,9 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { case "Follow": for _, e := range activity.To { - if res, err := db.GetActorFromDB(e); err == nil && res.Id != "" { + if res, err := activitypub.GetActorFromDB(e); err == nil && res.Id != "" { response := db.AcceptFollow(activity) - response, err := db.SetActorFollowerDB(response) + response, err := activitypub.SetActorFollowerDB(response) if err != nil { return err } @@ -735,12 +501,12 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { alreadyFollow := false alreadyFollowing := false - autoSub, err := db.GetActorAutoSubscribeDB(response.Actor.Id) + autoSub, err := activitypub.GetActorAutoSubscribeDB(response.Actor.Id) if err != nil { return err } - following, err := db.GetActorFollowingDB(response.Actor.Id) + following, err := activitypub.GetActorFollowingDB(response.Actor.Id) if err != nil { return err } @@ -785,7 +551,7 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request) error { return err } else { fmt.Println("follow request for rejected") - response := db.RejectActivity(activity) + response := activitypub.RejectActivity(activity) return db.MakeActivityRequest(response) } @@ -847,14 +613,14 @@ func IsMediaBanned(f multipart.File) (bool, error) { } func SendToFollowers(actor string, activity activitypub.Activity) error { - nActor, err := db.GetActorFromDB(actor) + nActor, err := activitypub.GetActorFromDB(actor) if err != nil { return err } activity.Actor = &nActor - followers, err := db.GetActorFollowDB(actor) + followers, err := activitypub.GetActorFollowDB(actor) if err != nil { return err } diff --git a/db/blacklist.go b/post/util.go index 4d88e10..c4920f7 100644 --- a/db/blacklist.go +++ b/post/util.go @@ -1,33 +1,38 @@ -package db +package post -import "regexp" +import ( + "mime/multipart" + "net/http" + "regexp" + + "github.com/FChannel0/FChannel-Server/config" +) type PostBlacklist struct { Id int Regex string } -func WriteRegexBlacklistDB(regex string) error { - query := `select from postblacklist where regex=$1` +func DeleteRegexBlacklistDB(id int) error { + query := `delete from postblacklist where id=$1` - rows, err := db.Query(query, regex) - if err != nil { - return err - } - defer rows.Close() + _, err := config.DB.Exec(query, id) + return err +} - var re string - rows.Next() - rows.Scan(&re) +func GetFileContentType(out multipart.File) (string, error) { + buffer := make([]byte, 512) - if re != "" { - return nil + _, err := out.Read(buffer) + if err != nil { + return "", err } - query = `insert into postblacklist (regex) values ($1)` + out.Seek(0, 0) - _, err = db.Exec(query, regex) - return err + contentType := http.DetectContentType(buffer) + + return contentType, nil } func GetRegexBlacklistDB() ([]PostBlacklist, error) { @@ -35,7 +40,7 @@ func GetRegexBlacklistDB() ([]PostBlacklist, error) { query := `select id, regex from postblacklist` - rows, err := db.Query(query) + rows, err := config.DB.Query(query) if err != nil { return list, err } @@ -51,15 +56,9 @@ func GetRegexBlacklistDB() ([]PostBlacklist, error) { return list, nil } -func DeleteRegexBlacklistDB(id int) error { - query := `delete from postblacklist where id=$1` - - _, err := db.Exec(query, id) - return err -} - func IsPostBlacklist(comment string) (bool, error) { postblacklist, err := GetRegexBlacklistDB() + if err != nil { return false, err } @@ -74,3 +73,21 @@ func IsPostBlacklist(comment string) (bool, error) { return false, nil } + +func WriteRegexBlacklistDB(regex string) error { + var re string + + query := `select from postblacklist where regex=$1` + if err := config.DB.QueryRow(query, regex).Scan(&re); err != nil { + return err + } + + if re != "" { + return nil + } + + query = `insert into postblacklist (regex) values ($1)` + + _, err := config.DB.Exec(query, regex) + return err +} diff --git a/routes/admin.go b/routes/admin.go index 528e40c..c714b06 100644 --- a/routes/admin.go +++ b/routes/admin.go @@ -3,6 +3,7 @@ package routes import ( "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" + "github.com/FChannel0/FChannel-Server/post" "github.com/FChannel0/FChannel-Server/webfinger" "github.com/gofiber/fiber/v2" ) @@ -49,11 +50,11 @@ func AdminIndex(ctx *fiber.Ctx) error { adminData.Board.ModCred, _ = db.GetPasswordFromSession(ctx) adminData.Title = actor.Name + " Admin page" - adminData.Boards = db.Boards + adminData.Boards = webfinger.Boards adminData.Board.Post.Actor = actor.Id - adminData.PostBlacklist, _ = db.GetRegexBlacklistDB() + adminData.PostBlacklist, _ = post.GetRegexBlacklistDB() adminData.Themes = &config.Themes diff --git a/routes/archive.go b/routes/archive.go index 87f3b8b..f3b4487 100644 --- a/routes/archive.go +++ b/routes/archive.go @@ -5,6 +5,7 @@ import ( "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" "github.com/FChannel0/FChannel-Server/util" + "github.com/FChannel0/FChannel-Server/webfinger" "github.com/gofiber/fiber/v2" ) @@ -29,7 +30,7 @@ func ArchiveGet(ctx *fiber.Ctx) error { returnData.Board.Post.Actor = actor.Id var err error - returnData.Instance, err = db.GetActorFromDB(config.Domain) + returnData.Instance, err = activitypub.GetActorFromDB(config.Domain) capt, err := db.GetRandomCaptcha() if err != nil { @@ -40,7 +41,7 @@ func ArchiveGet(ctx *fiber.Ctx) error { returnData.Title = "/" + actor.Name + "/ - " + actor.PreferredUsername - returnData.Boards = db.Boards + returnData.Boards = webfinger.Boards returnData.Posts = collection.OrderedItems diff --git a/routes/index.go b/routes/index.go index 23b3f39..015ad0c 100644 --- a/routes/index.go +++ b/routes/index.go @@ -1,22 +1,22 @@ package routes import ( + "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" - "github.com/FChannel0/FChannel-Server/util" "github.com/FChannel0/FChannel-Server/webfinger" "github.com/gofiber/fiber/v2" ) func Index(ctx *fiber.Ctx) error { - actor, err := db.GetActorFromDB(config.Domain) + actor, err := activitypub.GetActorFromDB(config.Domain) if err != nil { return err } // this is a activitpub json request return json instead of html page - if util.AcceptActivity(ctx.Get("Accept")) { - db.GetActorInfo(ctx, actor.Id) + if activitypub.AcceptActivity(ctx.Get("Accept")) { + activitypub.GetActorInfo(ctx, actor.Id) return nil } @@ -38,7 +38,7 @@ func Index(ctx *fiber.Ctx) error { data.Title = "Welcome to " + actor.PreferredUsername data.PreferredUsername = actor.PreferredUsername - data.Boards = db.Boards + data.Boards = webfinger.Boards data.Board.Name = "" data.Key = config.Key data.Board.Domain = config.Domain diff --git a/routes/news.go b/routes/news.go index 98d0019..2a8a0f9 100644 --- a/routes/news.go +++ b/routes/news.go @@ -1,8 +1,10 @@ package routes import ( + "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" + "github.com/FChannel0/FChannel-Server/webfinger" "github.com/gofiber/fiber/v2" ) @@ -10,14 +12,14 @@ func NewsGet(ctx *fiber.Ctx) error { // TODO timestamp := 0 - actor, err := db.GetActorFromDB(config.Domain) + actor, err := activitypub.GetActorFromDB(config.Domain) if err != nil { return err } var data PageData data.PreferredUsername = actor.PreferredUsername - data.Boards = db.Boards + data.Boards = webfinger.Boards data.Board.Name = "" data.Key = config.Key data.Board.Domain = config.Domain @@ -41,7 +43,7 @@ func NewsGet(ctx *fiber.Ctx) error { } func AllNewsGet(ctx *fiber.Ctx) error { - actor, err := db.GetActorFromDB(config.Domain) + actor, err := activitypub.GetActorFromDB(config.Domain) if err != nil { return err } @@ -49,7 +51,7 @@ func AllNewsGet(ctx *fiber.Ctx) error { var data PageData data.PreferredUsername = actor.PreferredUsername data.Title = actor.PreferredUsername + " News" - data.Boards = db.Boards + data.Boards = webfinger.Boards data.Board.Name = "" data.Key = config.Key data.Board.Domain = config.Domain diff --git a/routes/outbox.go b/routes/outbox.go index 4b874df..8049bd5 100644 --- a/routes/outbox.go +++ b/routes/outbox.go @@ -3,9 +3,11 @@ package routes import ( "strconv" + "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" "github.com/FChannel0/FChannel-Server/util" + "github.com/FChannel0/FChannel-Server/webfinger" "github.com/gofiber/fiber/v2" ) @@ -17,10 +19,10 @@ func Outbox(ctx *fiber.Ctx) error { func OutboxGet(ctx *fiber.Ctx) error { - actor := db.GetActorByName(ctx.Params("actor")) + actor := webfinger.GetActorByName(ctx.Params("actor")) - if util.AcceptActivity(ctx.Get("Accept")) { - db.GetActorInfo(ctx, actor.Id) + if activitypub.AcceptActivity(ctx.Get("Accept")) { + activitypub.GetActorInfo(ctx, actor.Id) return nil } @@ -79,7 +81,7 @@ func OutboxGet(ctx *fiber.Ctx) error { data.Key = config.Key - data.Boards = db.Boards + data.Boards = webfinger.Boards data.Posts = collection.OrderedItems data.Pages = pages diff --git a/routes/post.go b/routes/post.go index 8902f62..016e533 100644 --- a/routes/post.go +++ b/routes/post.go @@ -3,6 +3,7 @@ package routes import ( "regexp" + "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/config" "github.com/FChannel0/FChannel-Server/db" "github.com/FChannel0/FChannel-Server/util" @@ -11,7 +12,7 @@ import ( ) func PostGet(ctx *fiber.Ctx) error { - actor, err := db.GetActorByNameFromDB(ctx.Params("actor")) + actor, err := activitypub.GetActorByNameFromDB(ctx.Params("actor")) if err != nil { return err } @@ -25,14 +26,14 @@ func PostGet(ctx *fiber.Ctx) error { re := regexp.MustCompile("f(\\w|[!@#$%^&*<>])+-(\\w|[!@#$%^&*<>])+") if re.MatchString(postId) { // if non local actor post - name := util.GetActorFollowNameFromPath(postId) + name := activitypub.GetActorFollowNameFromPath(postId) followActors, err := webfinger.GetActorsFollowFromName(actor, name) if err != nil { return err } - followCollection, err := db.GetActorsFollowPostFromId(followActors, postId) + followCollection, err := activitypub.GetActorsFollowPostFromId(followActors, postId) if err != nil { return err } @@ -49,7 +50,7 @@ func PostGet(ctx *fiber.Ctx) error { data.Board.Post.Actor = actor.Id } } else { - collection, err := db.GetObjectByIDFromDB(inReplyTo) + collection, err := activitypub.GetObjectByIDFromDB(inReplyTo) if err != nil { return err } @@ -85,13 +86,13 @@ func PostGet(ctx *fiber.Ctx) error { data.Board.Captcha = config.Domain + "/" + capt data.Board.CaptchaCode = util.GetCaptchaCode(data.Board.Captcha) - data.Instance, err = db.GetActorFromDB(config.Domain) + data.Instance, err = activitypub.GetActorFromDB(config.Domain) if err != nil { return err } data.Key = config.Key - data.Boards = db.Boards + data.Boards = webfinger.Boards data.Title = "/" + data.Board.Name + "/ - " + data.PostId @@ -112,12 +113,12 @@ func PostGet(ctx *fiber.Ctx) error { func CatalogGet(ctx *fiber.Ctx) error { actorName := ctx.Params("actor") - actor, err := db.GetActorByNameFromDB(actorName) + actor, err := activitypub.GetActorByNameFromDB(actorName) if err != nil { return err } - collection, err := db.GetObjectFromDBCatalog(actor.Id) + collection, err := activitypub.GetObjectFromDBCatalog(actor.Id) // TODO: implement this in template functions // "showArchive": func() bool { @@ -148,7 +149,7 @@ func CatalogGet(ctx *fiber.Ctx) error { data.Board.Post.Actor = actor.Id - data.Instance, err = db.GetActorFromDB(config.Domain) + data.Instance, err = activitypub.GetActorFromDB(config.Domain) if err != nil { return err } @@ -163,7 +164,7 @@ func CatalogGet(ctx *fiber.Ctx) error { data.Title = "/" + data.Board.Name + "/ - catalog" - data.Boards = db.Boards + data.Boards = webfinger.Boards data.Posts = collection.OrderedItems data.Meta.Description = data.Board.Summary diff --git a/routes/structs.go b/routes/structs.go index 8905e65..8cfb6ee 100644 --- a/routes/structs.go +++ b/routes/structs.go @@ -3,16 +3,18 @@ package routes import ( "github.com/FChannel0/FChannel-Server/activitypub" "github.com/FChannel0/FChannel-Server/db" + "github.com/FChannel0/FChannel-Server/post" + "github.com/FChannel0/FChannel-Server/webfinger" ) type PageData struct { Title string PreferredUsername string - Board db.Board + Board webfinger.Board Pages []int CurrentPage int TotalPage int - Boards []db.Board + Boards []webfinger.Board Posts []activitypub.ObjectBase Key string PostId string @@ -29,16 +31,16 @@ type PageData struct { type AdminPage struct { Title string - Board db.Board + Board webfinger.Board Key string Actor string - Boards []db.Board + Boards []webfinger.Board Following []string Followers []string Reported []db.Report Domain string IsLocal bool - PostBlacklist []db.PostBlacklist + PostBlacklist []post.PostBlacklist AutoSubscribe bool Themes *[]string diff --git a/routes/util.go b/routes/util.go index 3d03795..ef7d379 100644 --- a/routes/util.go +++ b/routes/util.go @@ -53,13 +53,13 @@ func wantToServePage(actorName string, page int) (activitypub.Collection, bool, return collection, serve, ErrorPageLimit } - actor, err := db.GetActorByNameFromDB(actorName) + actor, err := activitypub.GetActorByNameFromDB(actorName) if err != nil { return collection, false, err } if actor.Id != "" { - collection, err = db.GetObjectFromDBPage(actor.Id, page) + collection, err = activitypub.GetObjectFromDBPage(actor.Id, page) if err != nil { return collection, false, err } @@ -75,13 +75,13 @@ func wantToServeCatalog(actorName string) (activitypub.Collection, bool, error) var collection activitypub.Collection serve := false - actor, err := db.GetActorByNameFromDB(actorName) + actor, err := activitypub.GetActorByNameFromDB(actorName) if err != nil { return collection, false, err } if actor.Id != "" { - collection, err = db.GetObjectFromDBCatalog(actor.Id) + collection, err = activitypub.GetObjectFromDBCatalog(actor.Id) if err != nil { return collection, false, err } @@ -97,13 +97,13 @@ func wantToServeArchive(actorName string) (activitypub.Collection, bool, error) var collection activitypub.Collection serve := false - actor, err := db.GetActorByNameFromDB(actorName) + actor, err := activitypub.GetActorByNameFromDB(actorName) if err != nil { return collection, false, err } if actor.Id != "" { - collection, err = db.GetActorCollectionDBType(actor.Id, "Archive") + collection, err = activitypub.GetActorCollectionDBType(actor.Id, "Archive") if err != nil { return collection, false, err } diff --git a/util/accept.go b/util/accept.go deleted file mode 100644 index 2765c32..0000000 --- a/util/accept.go +++ /dev/null @@ -1,21 +0,0 @@ -package util - -import ( - "regexp" - "strings" -) - -// False positive for application/ld+ld, application/activity+ld, application/json+json -var activityRegexp = regexp.MustCompile("application\\/(ld|json|activity)((\\+(ld|json))|$)") - -func AcceptActivity(header string) bool { - accept := false - if strings.Contains(header, ";") { - split := strings.Split(header, ";") - accept = accept || activityRegexp.MatchString(split[0]) - accept = accept || strings.Contains(split[len(split)-1], "profile=\"https://www.w3.org/ns/activitystreams\"") - } else { - accept = accept || activityRegexp.MatchString(header) - } - return accept -} diff --git a/util/util.go b/util/util.go index 1feb53a..381db94 100644 --- a/util/util.go +++ b/util/util.go @@ -7,6 +7,8 @@ import ( "os" "regexp" "strings" + + "github.com/FChannel0/FChannel-Server/config" ) func IsOnion(url string) bool { @@ -18,56 +20,6 @@ func IsOnion(url string) bool { return false } -func GetActorInstance(path string) (string, string) { - re := regexp.MustCompile(`([@]?([\w\d.-_]+)[@](.+))`) - atFormat := re.MatchString(path) - - if atFormat { - match := re.FindStringSubmatch(path) - if len(match) > 2 { - return match[2], match[3] - } - } - - re = regexp.MustCompile(`(https?://)?(www)?([\w\d-_.:]+)(/|\s+|\r|\r\n)?$`) - mainActor := re.MatchString(path) - if mainActor { - match := re.FindStringSubmatch(path) - if len(match) > 2 { - return "main", match[3] - } - } - - re = regexp.MustCompile(`(https?://)?(www)?([\w\d-_.:]+)\/([\w\d-_.]+)(\/([\w\d-_.]+))?`) - httpFormat := re.MatchString(path) - - if httpFormat { - match := re.FindStringSubmatch(path) - if len(match) > 3 { - if match[4] == "users" { - return match[6], match[3] - } - - return match[4], match[3] - } - } - - return "", "" -} - -func GetActorFollowNameFromPath(path string) string { - var actor string - - re := regexp.MustCompile("f\\w+-") - - actor = re.FindString(path) - - actor = strings.Replace(actor, "f", "", 1) - actor = strings.Replace(actor, "-", "", 1) - - return actor -} - func StripTransferProtocol(value string) string { re := regexp.MustCompile("(http://|https://)?(www.)?") @@ -245,3 +197,31 @@ func EscapeString(text string) string { text = strings.Replace(text, "<", "<", -1) return text } + +func CreateUniqueID(actor string) (string, error) { + var newID string + isUnique := false + for !isUnique { + newID = RandomID(8) + + query := "select id from activitystream where id=$1" + args := fmt.Sprintf("%s/%s/%s", config.Domain, actor, newID) + rows, err := config.DB.Query(query, args) + if err != nil { + return "", err + } + + defer rows.Close() + + // reusing a variable here + // if we encounter a match, it'll get set to false causing the outer for loop to loop and to go through this all over again + // however if nothing is there, it'll remain true and exit the loop + isUnique = true + for rows.Next() { + isUnique = false + break + } + } + + return newID, nil +} diff --git a/db/boards.go b/webfinger/util.go index b54ab60..cd16943 100644 --- a/db/boards.go +++ b/webfinger/util.go @@ -1,10 +1,12 @@ -package db +package webfinger import ( + "fmt" + "regexp" "sort" + "strings" "github.com/FChannel0/FChannel-Server/activitypub" - "github.com/FChannel0/FChannel-Server/webfinger" ) var Boards []Board @@ -34,17 +36,29 @@ func (a BoardSortAsc) Len() int { return len(a) } func (a BoardSortAsc) Less(i, j int) bool { return a[i].Name < a[j].Name } func (a BoardSortAsc) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func GetActorByName(name string) activitypub.Actor { + var actor activitypub.Actor + boards, _ := GetBoardCollection() + for _, e := range boards { + if e.Actor.Name == name { + actor = e.Actor + } + } + + return actor +} + func GetBoardCollection() ([]Board, error) { var collection []Board for _, e := range FollowingBoards { var board Board - boardActor, err := GetActorFromDB(e.Id) + boardActor, err := activitypub.GetActorFromDB(e.Id) if err != nil { return collection, err } if boardActor.Id == "" { - boardActor, err = webfinger.FingerActor(e.Id) + boardActor, err = FingerActor(e.Id) if err != nil { return collection, err } @@ -62,3 +76,34 @@ func GetBoardCollection() ([]Board, error) { return collection, nil } + +func GetActorFromPath(location string, prefix string) (activitypub.Actor, error) { + 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 activitypub.Actor + + nActor, err := activitypub.GetActorByNameFromDB(actor) + if err != nil { + return nActor, err + } + + if nActor.Id == "" { + nActor = GetActorByName(actor) + } + + return nActor, nil +} diff --git a/webfinger/webfinger.go b/webfinger/webfinger.go index d937ae4..210a3d6 100644 --- a/webfinger/webfinger.go +++ b/webfinger/webfinger.go @@ -32,7 +32,7 @@ func GetActor(id string) (activitypub.Actor, error) { return respActor, nil } - actor, instance := util.GetActorInstance(id) + actor, instance := activitypub.GetActorInstance(id) if ActorCache[actor+"@"+instance].Id != "" { respActor = ActorCache[actor+"@"+instance] @@ -69,7 +69,7 @@ func GetActor(id string) (activitypub.Actor, error) { func FingerActor(path string) (activitypub.Actor, error) { var nActor activitypub.Actor - actor, instance := util.GetActorInstance(path) + actor, instance := activitypub.GetActorInstance(path) if actor == "" && instance == "" { return nActor, nil |