From f7bf818d29393ceaccf4d2906557351fa6a4f49f Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sat, 7 May 2022 21:21:38 -0700 Subject: added error func and general cleanup/organization --- activitypub/activity.go | 360 ++------------------ activitypub/actor.go | 665 ++++++++++--------------------------- activitypub/object.go | 848 ++++++++++++++++++++---------------------------- activitypub/pem.go | 39 +-- activitypub/util.go | 489 ++++++++++++++++++++++++++++ 5 files changed, 1079 insertions(+), 1322 deletions(-) create mode 100644 activitypub/util.go (limited to 'activitypub') diff --git a/activitypub/activity.go b/activitypub/activity.go index 88d9973..06e061b 100644 --- a/activitypub/activity.go +++ b/activitypub/activity.go @@ -1,20 +1,14 @@ package activitypub import ( - "encoding/json" "errors" "fmt" - "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, ";") { @@ -27,169 +21,7 @@ func AcceptActivity(header string) bool { return accept } -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 - - if err := rows.Scan(&nColl.Actor.Id, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &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 = post.GetReplies() - if err != nil { - return nColl, err - } - - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, err = post.Attachment[0].GetAttachment() - if err != nil { - return nColl, err - } - - post.Preview, err = post.Preview.GetPreview() - 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) { +func (activity Activity) IsLocal() (bool, error) { for _, e := range activity.To { if res, _ := GetActorFromDB(e); res.Id != "" { @@ -212,15 +44,15 @@ func IsActivityLocal(activity Activity) (bool, error) { return false, nil } -func ProcessActivity(activity Activity) error { +func (activity Activity) Process() error { activityType := activity.Type if activityType == "Create" { for _, e := range activity.To { - if res, err := GetActorFromDB(e); err == nil && res.Id != "" { + if res, err := GetActorFromDB(e); res.Id != "" { fmt.Println("actor is in the database") } else if err != nil { - return err + return util.MakeError(err, "Process") } else { fmt.Println("actor is NOT in the database") } @@ -235,7 +67,7 @@ func ProcessActivity(activity Activity) error { return nil } -func RejectActivity(activity Activity) Activity { +func (activity Activity) Reject() Activity { var accept Activity accept.AtContext.Context = activity.AtContext.Context accept.Type = "Reject" @@ -255,184 +87,56 @@ func RejectActivity(activity Activity) Activity { } func (activity Activity) Report(reason string) (bool, error) { - if res, err := activity.Object.IsLocal(); err == nil && !res { - // TODO: not local error - return false, nil - } else if err != nil { - return false, err - } - - activityCol, err := GetActivityFromDB(activity.Object.Id) - if err != nil { - return false, err + if isLocal, err := activity.Object.IsLocal(); !isLocal || err != nil { + return false, util.MakeError(err, "Report") } - query := `select count from reported where id=$1` - - rows, err := config.DB.Query(query, activity.Object.Id) + activityCol, err := activity.Object.GetCollection() if err != nil { - return false, err + return false, util.MakeError(err, "Report") } - 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, activity.Object.Object.Id, 1, activityCol.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, activity.Object.Id) - if err != nil { - return false, err - } + query := `insert into reported (id, count, board, reason) values ($1, $2, $3, $4)` + if _, err = config.DB.Exec(query, activity.Object.Object.Id, 1, activityCol.Actor.Id, reason); err != nil { + return false, util.MakeError(err, "Report") } return true, nil } -func (obj ObjectBase) WriteCache() error { - obj.Name = util.EscapeString(obj.Name) - obj.Content = util.EscapeString(obj.Content) - obj.AttributedTo = util.EscapeString(obj.AttributedTo) +func (activity Activity) SetFollower() (Activity, error) { + var query string - query := `select id from cacheactivitystream where id=$1` - - rows, err := config.DB.Query(query, obj.Id) - if err != nil { - return err - } - defer rows.Close() + alreadyFollow, err := activity.Actor.IsAlreadyFollower(activity.Object.Actor) - var id string - rows.Next() - err = rows.Scan(&id) if err != nil { - return err - } else if id != "" { - return nil // TODO: error? + return activity, util.MakeError(err, "SetFollower") } - if obj.Updated.IsZero() { - obj.Updated = obj.Published + activity.Type = "Reject" + if activity.Actor.Id == activity.Object.Actor { + return activity, nil } - 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 (obj ObjectBase) WriteCacheWithAttachment(attachment 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 alreadyFollow { + query = `delete from follower where id=$1 and follower=$2` + activity.Summary = activity.Object.Actor + " Unfollow " + activity.Actor.Id - 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, obj.Preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) - return err -} - -func (obj ObjectBase) WriteWithAttachment(attachment ObjectBase) { - - 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, obj.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 (obj ObjectBase) WriteAttachmentCache() error { - var id string - - query := `select id from cacheactivitystream where id=$1` - if err := config.DB.QueryRow(query, obj.Id).Scan(&id); err != nil { - if obj.Updated.IsZero() { - obj.Updated = obj.Published + if _, err := config.DB.Exec(query, activity.Actor.Id, activity.Object.Actor); err != nil { + return activity, util.MakeError(err, "SetFollower") } - 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 - } - - return nil -} - -func (obj ObjectBase) WriteAttachment() { - 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) + activity.Type = "Accept" + return activity, util.MakeError(err, "SetFollower") } -} -func (obj NestedObjectBase) WritePreviewCache() error { - var id string + query = `insert into follower (id, follower) values ($1, $2)` + activity.Summary = activity.Object.Actor + " Follow " + activity.Actor.Id - query := `select id from cacheactivitystream where id=$1` - err := config.DB.QueryRow(query, obj.Id).Scan(&id) - if err != nil { - 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 + if _, err := config.DB.Exec(query, activity.Actor.Id, activity.Object.Actor); err != nil { + return activity, util.MakeError(err, "SetFollower") } - return nil -} - -func (obj NestedObjectBase) WritePreview() 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 + activity.Type = "Accept" + return activity, nil } diff --git a/activitypub/actor.go b/activitypub/actor.go index bec0bce..120c9fd 100644 --- a/activitypub/actor.go +++ b/activitypub/actor.go @@ -13,10 +13,7 @@ import ( "errors" "fmt" "io/ioutil" - "log" - "net/http" "os" - "regexp" "strings" "github.com/FChannel0/FChannel-Server/config" @@ -24,54 +21,23 @@ import ( "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 (actor Actor) AddFollower(follower string) error { query := `insert into follower (id, follower) values ($1, $2)` _, err := config.DB.Exec(query, actor.Id, follower) - return err + return util.MakeError(err, "AddFollwer") } func (actor Actor) ActivitySign(signature string) (string, error) { - query := `select file from publicKeyPem where id=$1 ` + var file string - rows, err := config.DB.Query(query, actor.PublicKey.Id) - if err != nil { - return "", err + query := `select file from publicKeyPem where id=$1 ` + if err := config.DB.QueryRow(query, actor.PublicKey.Id).Scan(&file); err != nil { + return "", util.MakeError(err, "ActivitySign") } - defer rows.Close() - - var file string - rows.Next() - rows.Scan(&file) - file = strings.ReplaceAll(file, "public.pem", "private.pem") - _, err = os.Stat(file) + + _, 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 @@ -80,16 +46,16 @@ 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 "", util.MakeError(err, "ActivitySign") } - publickey, err := ioutil.ReadFile(file) - if err != nil { - return "", err + var publickey []byte + + if publickey, err = ioutil.ReadFile(file); err != nil { + return "", util.MakeError(err, "ActivitySign") } block, _ := pem.Decode(publickey) - pub, _ := x509.ParsePKCS1PrivateKey(block.Bytes) rng := crand.Reader hashed := sha256.New() @@ -101,23 +67,21 @@ accepting your posts from your board from this site. Good luck ;)`) func (actor Actor) DeleteCache() error { query := `select id from cacheactivitystream where id in (select id from cacheactivitystream where actor=$1)` - rows, err := config.DB.Query(query, actor.Id) if err != nil { - return err + return util.MakeError(err, "DeleteCache") } defer rows.Close() - for rows.Next() { var obj ObjectBase if err := rows.Scan(&obj.Id); err != nil { - return err + return util.MakeError(err, "DeleteCache") } if err := obj.Delete(); err != nil { - return err + return util.MakeError(err, "DeleteCache") } } @@ -129,7 +93,7 @@ func (actor Actor) GetAutoSubscribe() (bool, error) { query := `select autosubscribe from actor where id=$1` if err := config.DB.QueryRow(query, actor.Id).Scan(&subscribed); err != nil { - return false, err + return false, util.MakeError(err, "GetAutoSubscribe") } return subscribed, nil @@ -140,10 +104,9 @@ func (actor Actor) GetCollectionType(nType string) (Collection, error) { 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, actor.Id, nType) if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionType") } defer rows.Close() @@ -158,7 +121,7 @@ func (actor Actor) GetCollectionType(nType string) (Collection, error) { post.Preview = &prev if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionType") } post.Actor = actor.Id @@ -167,21 +130,19 @@ func (actor Actor) GetCollectionType(nType string) (Collection, error) { post.Replies = replies - var err error - post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesCount() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionType") } post.Attachment, err = post.Attachment[0].GetAttachment() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionType") } post.Preview, err = post.Preview.GetPreview() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionType") } result = append(result, post) @@ -197,10 +158,10 @@ func (actor Actor) GetCollectionTypeLimit(nType string, limit int) (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, actor.Id, nType, limit) + if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionTypeLimit") } defer rows.Close() @@ -215,7 +176,7 @@ func (actor Actor) GetCollectionTypeLimit(nType string, limit int) (Collection, post.Preview = &prev if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionTypeLimit") } post.Actor = actor.Id @@ -224,20 +185,19 @@ func (actor Actor) GetCollectionTypeLimit(nType string, limit int) (Collection, post.Replies = replies - var err error post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesCount() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionTypeLimit") } post.Attachment, err = post.Attachment[0].GetAttachment() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionTypeLimit") } post.Preview, err = post.Preview.GetPreview() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionTypeLimit") } result = append(result, post) @@ -252,18 +212,18 @@ func (actor Actor) GetFollow() ([]ObjectBase, error) { var followerCollection []ObjectBase query := `select follower from follower where id=$1` - rows, err := config.DB.Query(query, actor.Id) + if err != nil { - return followerCollection, err + return followerCollection, util.MakeError(err, "GetFollow") } - defer rows.Close() + defer rows.Close() for rows.Next() { var obj ObjectBase if err := rows.Scan(&obj.Id); err != nil { - return followerCollection, err + return followerCollection, util.MakeError(err, "GetFollow") } followerCollection = append(followerCollection, obj) @@ -277,7 +237,7 @@ func (actor Actor) GetFollowingTotal() (int, error) { query := `select count(following) from following where id=$1` if err := config.DB.QueryRow(query, actor.Id).Scan(&following); err != nil { - return following, err + return following, util.MakeError(err, "GetFollowingTotal") } return following, nil @@ -288,7 +248,7 @@ func (actor Actor) GetFollowersTotal() (int, error) { query := `select count(follower) from follower where id=$1` if err := config.DB.QueryRow(query, actor.Id).Scan(&followers); err != nil { - return followers, err + return followers, util.MakeError(err, "GetFollowersTotal") } return followers, nil @@ -301,18 +261,21 @@ func (actor Actor) GetFollowersResp(ctx *fiber.Ctx) error { following.AtContext.Context = "https://www.w3.org/ns/activitystreams" following.Type = "Collection" following.TotalItems, err = actor.GetFollowingTotal() + if err != nil { - return err + return util.MakeError(err, "GetFollowersResp") } following.Items, err = actor.GetFollow() + if err != nil { - return err + return util.MakeError(err, "GetFollowersResp") } enc, _ := json.MarshalIndent(following, "", "\t") ctx.Response().Header.Set("Content-Type", config.ActivityStreams) _, err = ctx.Write(enc) + return err } @@ -323,37 +286,40 @@ func (actor Actor) GetFollowingResp(ctx *fiber.Ctx) error { following.AtContext.Context = "https://www.w3.org/ns/activitystreams" following.Type = "Collection" following.TotalItems, err = actor.GetFollowingTotal() + if err != nil { - return err + return util.MakeError(err, "GetFollowingResp") } following.Items, err = actor.GetFollowing() + if err != nil { - return err + return util.MakeError(err, "GetFollowingResp") } enc, _ := json.MarshalIndent(following, "", "\t") ctx.Response().Header.Set("Content-Type", config.ActivityStreams) _, err = ctx.Write(enc) - return err + return util.MakeError(err, "GetFollowingResp") } func (actor Actor) GetFollowing() ([]ObjectBase, error) { var followingCollection []ObjectBase - query := `select following from following where id=$1` + query := `select following from following where id=$1` rows, err := config.DB.Query(query, actor.Id) + if err != nil { - return followingCollection, err + return followingCollection, util.MakeError(err, "GetFollowing") } - defer rows.Close() + defer rows.Close() for rows.Next() { var obj ObjectBase if err := rows.Scan(&obj.Id); err != nil { - return followingCollection, err + return followingCollection, util.MakeError(err, "GetFollowing") } followingCollection = append(followingCollection, obj) @@ -368,7 +334,7 @@ func (actor Actor) GetInfoResp(ctx *fiber.Ctx) error { _, err := ctx.Write(enc) - return err + return util.MakeError(err, "GetInfoResp") } func (actor Actor) GetCollection() (Collection, error) { @@ -376,10 +342,10 @@ func (actor Actor) GetCollection() (Collection, error) { 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, actor.Id) + if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } defer rows.Close() @@ -394,34 +360,34 @@ func (actor Actor) GetCollection() (Collection, error) { post.Preview = &prev if err := rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } post.Actor = actor.Id - var postCnt int - var imgCnt int - post.Replies, postCnt, imgCnt, err = post.GetReplies() + post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetReplies() + if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - post.Attachment, err = post.Attachment[0].GetAttachment() + if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } post.Preview, err = post.Preview.GetPreview() + if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } result = append(result, post) } + nColl.AtContext.Context = "https://www.w3.org/ns/activitystreams" + nColl.OrderedItems = result return nColl, nil @@ -431,19 +397,22 @@ func (actor Actor) GetRecentPosts() ([]ObjectBase, error) { var collection []ObjectBase query := `select id, actor, content, published, attachment from activitystream where actor=$1 and type='Note' union select id, actor, content, published, attachment from cacheactivitystream where actor in (select follower from follower where id=$1) and type='Note' order by published desc limit 20` - rows, err := config.DB.Query(query, actor.Id) if err != nil { - log.Println("Could not get recent posts") - return collection, err + return collection, util.MakeError(err, "GetRecentPosts") } defer rows.Close() for rows.Next() { var nObj ObjectBase var attachment ObjectBase - rows.Scan(&nObj.Id, &nObj.Actor, &nObj.Content, &nObj.Published, &attachment.Id) + + err := rows.Scan(&nObj.Id, &nObj.Actor, &nObj.Content, &nObj.Published, &attachment.Id) + + if err != nil { + return collection, util.MakeError(err, "GetRecentPosts") + } isOP, _ := nObj.CheckIfOP() @@ -465,18 +434,21 @@ func (actor Actor) GetReported() ([]ObjectBase, error) { var nObj []ObjectBase query := `select id, count, reason from reported where board=$1` - rows, err := config.DB.Query(query, actor.Id) + if err != nil { - return nObj, err + return nObj, util.MakeError(err, "GetReported") } defer rows.Close() - for rows.Next() { var obj ObjectBase - rows.Scan(&obj.Id, &obj.Size, &obj.Content) + err := rows.Scan(&obj.Id, &obj.Size, &obj.Content) + + if err != nil { + return nObj, util.MakeError(err, "GetReported") + } nObj = append(nObj, obj) } @@ -489,7 +461,7 @@ func (actor Actor) GetReportedTotal() (int, error) { query := `select count(id) from reported where board=$1` if err := config.DB.QueryRow(query, actor.Id).Scan(&count); err != nil { - return 0, err + return 0, util.MakeError(err, "GetReportedTotal") } return count, nil @@ -500,18 +472,18 @@ func (actor Actor) GetAllArchive(offset int) (Collection, error) { 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, actor.Id, offset) + if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetAllArchive") } - defer rows.Close() + defer rows.Close() for rows.Next() { var post ObjectBase if err := rows.Scan(&post.Id, &post.Updated); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetAllArchive") } post.Replies, _, _, err = post.GetReplies() @@ -519,6 +491,8 @@ func (actor Actor) GetAllArchive(offset int) (Collection, error) { result = append(result, post) } + nColl.AtContext.Context = "https://www.w3.org/ns/activitystreams" + nColl.OrderedItems = result return nColl, nil @@ -526,8 +500,9 @@ func (actor Actor) GetAllArchive(offset int) (Collection, error) { func (actor Actor) IsAlreadyFollowing(follow string) (bool, error) { followers, err := actor.GetFollowing() + if err != nil { - return false, err + return false, util.MakeError(err, "IsAlreadyFollowing") } for _, e := range followers { @@ -541,8 +516,9 @@ func (actor Actor) IsAlreadyFollowing(follow string) (bool, error) { func (actor Actor) IsAlreadyFollower(follow string) (bool, error) { followers, err := actor.GetFollow() + if err != nil { - return false, err + return false, util.MakeError(err, "IsAlreadyFollower") } for _, e := range followers { @@ -556,413 +532,74 @@ func (actor Actor) IsAlreadyFollower(follow string) (bool, error) { func (actor Actor) SetActorAutoSubscribeDB() error { current, err := actor.GetAutoSubscribe() + if err != nil { - return err + return util.MakeError(err, "SetActorAutoSubscribeDB") } query := `update actor set autosubscribe=$1 where id=$2` - _, err = config.DB.Exec(query, !current, actor.Id) - return err + + return util.MakeError(err, "SetActorAutoSubscribeDB") } func (actor Actor) GetOutbox(ctx *fiber.Ctx) error { - var collection Collection c, err := actor.GetCollection() + if err != nil { - return err + return util.MakeError(err, "GetOutbox") } - collection.OrderedItems = c.OrderedItems + collection.OrderedItems = c.OrderedItems collection.AtContext.Context = "https://www.w3.org/ns/activitystreams" collection.Actor = actor - collection.TotalItems, err = GetObjectPostsTotalDB(actor) + collection.TotalItems, err = actor.GetPostTotal() + if err != nil { - return err + return util.MakeError(err, "GetOutbox") } - collection.TotalImgs, err = GetObjectImgsTotalDB(actor) + collection.TotalImgs, err = actor.GetImgTotal() + if err != nil { - return err + return util.MakeError(err, "GetOutbox") } enc, _ := json.Marshal(collection) - ctx.Response().Header.Set("Content-Type", config.ActivityStreams) _, err = ctx.Write(enc) - return err + + return util.MakeError(err, "GetOutbox") } func (actor Actor) UnArchiveLast() error { col, err := actor.GetCollectionTypeLimit("Archive", 1) + if err != nil { - return err + return util.MakeError(err, "UnArchiveLast") } for _, e := range col.OrderedItems { for _, k := range e.Replies.OrderedItems { if err := k.UpdateType("Note"); err != nil { - return err + return util.MakeError(err, "UnArchiveLast") } } if err := e.UpdateType("Note"); err != nil { - return err + return util.MakeError(err, "UnArchiveLast") } } return nil } -func GetActorByNameFromDB(name string) (Actor, error) { - var nActor Actor - var publicKeyPem string - - query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1` - err := config.DB.QueryRow(query, name).Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &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 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 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 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` - - var publicKeyPem string - err := config.DB.QueryRow(query, id).Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) - if 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 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 GetActorsFollowPostFromId(actors []string, id string) (Collection, error) { - var collection Collection - - for _, e := range actors { - obj := ObjectBase{Id: e + "/" + id} - tempCol, err := obj.GetCollectionFromPath() - if err != nil { - return collection, err - } - - if len(tempCol.OrderedItems) > 0 { - collection = tempCol - return collection, nil - } - } - - return collection, nil -} - -func GetActorPost(ctx *fiber.Ctx, path string) error { - obj := ObjectBase{Id: config.Domain + "" + path} - collection, err := obj.GetCollectionFromPath() - if err != nil { - return err - } - - if len(collection.OrderedItems) > 0 { - enc, err := json.MarshalIndent(collection, "", "\t") - if err != nil { - return err - } - - ctx.Response().Header.Set("Content-Type", "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") - _, err = ctx.Write(enc) - return err - } - - return 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 SetActorFollowerDB(activity Activity) (Activity, error) { - var query string - alreadyFollow, err := activity.Actor.IsAlreadyFollower(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 (obj ObjectBase) WriteActorObjectReplyToDB() 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 (obj ObjectBase) WriteActorObjectToCache() (ObjectBase, error) { - if res, err := util.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 := obj.IsLocal(); err == nil && res { - return obj, err - } else if err != nil { - return obj, err - } - - if obj.Preview.Href != "" { - obj.Preview.WritePreviewCache() - } - - for i, _ := range obj.Attachment { - obj.Attachment[i].WriteAttachmentCache() - obj.WriteCacheWithAttachment(obj.Attachment[i]) - } - - } else { - if err := obj.WriteCache(); err != nil { - return obj, err - } - } - - obj.WriteActorObjectReplyToDB() - - if obj.Replies.OrderedItems != nil { - for _, e := range obj.Replies.OrderedItems { - e.WriteActorObjectToCache() - } - } - - return obj, nil +func (actor Actor) IsLocal() (bool, error) { + actor, err := GetActorFromDB(actor.Id) + return actor.Id != "", util.MakeError(err, "IsLocal") } func (actor Actor) GetCatalogCollection() (Collection, error) { @@ -973,9 +610,8 @@ func (actor Actor) GetCatalogCollection() (Collection, error) { var rows *sql.Rows 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` - if rows, err = config.DB.Query(query, actor.Id); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCatalogCollection") } defer rows.Close() @@ -992,7 +628,7 @@ func (actor Actor) GetCatalogCollection() (Collection, error) { err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive) if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCatalogCollection") } post.Actor = actor.Id @@ -1001,11 +637,23 @@ func (actor Actor) GetCatalogCollection() (Collection, error) { post.Replies = replies - post.Replies.TotalItems, post.Replies.TotalImgs, _ = post.GetRepliesCount() + post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesCount() + + if err != nil { + return nColl, util.MakeError(err, "GetCatalogCollection") + } + + post.Attachment, err = post.Attachment[0].GetAttachment() - post.Attachment, _ = post.Attachment[0].GetAttachment() + if err != nil { + return nColl, util.MakeError(err, "GetCatalogCollection") + } - post.Preview, _ = post.Preview.GetPreview() + post.Preview, err = post.Preview.GetPreview() + + if err != nil { + return nColl, util.MakeError(err, "GetCatalogCollection") + } result = append(result, post) } @@ -1027,7 +675,7 @@ func (actor Actor) GetCollectionPage(page int) (Collection, error) { 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` if rows, err = config.DB.Query(query, actor.Id, page*15); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionPage") } var count int @@ -1045,22 +693,28 @@ func (actor Actor) GetCollectionPage(page int) (Collection, error) { err = rows.Scan(&count, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive) if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionPage") } post.Actor = actor.Id - var postCnt int - var imgCnt int + post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesLimit(5) + + if err != nil { + return nColl, util.MakeError(err, "GetCollectionPage") + } - post.Replies, postCnt, imgCnt, _ = post.GetRepliesLimit(5) + post.Attachment, err = post.Attachment[0].GetAttachment() - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt + if err != nil { + return nColl, util.MakeError(err, "GetCollectionPage") + } - post.Attachment, _ = post.Attachment[0].GetAttachment() + post.Preview, err = post.Preview.GetPreview() - post.Preview, _ = post.Preview.GetPreview() + if err != nil { + return nColl, util.MakeError(err, "GetCollectionPage") + } result = append(result, post) } @@ -1073,3 +727,42 @@ func (actor Actor) GetCollectionPage(page int) (Collection, error) { return nColl, nil } + +func (actor Actor) WantToServePage(page int) (Collection, error) { + var collection Collection + var err error + + if page > config.PostCountPerPage { + return collection, errors.New("above page limit") + } + + if collection, err = actor.GetCollectionPage(page); err != nil { + return collection, util.MakeError(err, "WantToServePage") + } + + collection.Actor = actor + + return collection, nil +} + +func (actor Actor) GetImgTotal() (int, error) { + var count int + + query := `select count(attachment) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note' )` + if err := config.DB.QueryRow(query, actor.Id).Scan(&count); err != nil { + return count, util.MakeError(err, "GetImgTotal") + } + + return count, nil +} + +func (actor Actor) GetPostTotal() (int, error) { + var count int + + query := `select count(id) from activitystream where actor=$1 and id in (select id from replies where inreplyto='' and type='Note')` + if err := config.DB.QueryRow(query, actor.Id).Scan(&count); err != nil { + return count, util.MakeError(err, "GetPostTotal") + } + + return count, nil +} diff --git a/activitypub/object.go b/activitypub/object.go index 107fe0e..a873ce0 100644 --- a/activitypub/object.go +++ b/activitypub/object.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "mime/multipart" "net/http" "os" "os/exec" @@ -21,73 +20,26 @@ import ( func (obj ObjectBase) CheckIfOP() (bool, error) { var count int - query := `select count(id) from replies where inreplyto='' and id=$1 ` - + query := `select id from replies where inreplyto='' and id=$1 ` if err := config.DB.QueryRow(query, obj.Id).Scan(&count); err != nil { - return false, err + return false, nil } return true, nil } -func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]ObjectBase, *os.File, error) { - contentType, err := util.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 (obj ObjectBase) CreatePreview() *NestedObjectBase { - re := regexp.MustCompile(`/.+$`) + var nPreview 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" @@ -98,7 +50,6 @@ func (obj ObjectBase) CreatePreview() *NestedObjectBase { nPreview.Published = obj.Published re = regexp.MustCompile(`/public/.+`) - objFile := re.FindString(obj.Href) cmd := exec.Command("convert", "."+objFile, "-resize", "250x250>", "-strip", "."+href) @@ -116,12 +67,12 @@ func (obj ObjectBase) CreatePreview() *NestedObjectBase { func (obj ObjectBase) DeleteAttachment() error { query := `delete from activitystream where id in (select attachment from activitystream where id=$1)` if _, err := config.DB.Exec(query, obj.Id); err != nil { - return err + return util.MakeError(err, "DeleteAttachment") } query = `delete from cacheactivitystream where id in (select attachment from cacheactivitystream where id=$1)` _, err := config.DB.Exec(query, obj.Id) - return err + return util.MakeError(err, "DeleteAttachment") } func (obj ObjectBase) DeleteAttachmentFromFile() error { @@ -129,13 +80,13 @@ func (obj ObjectBase) DeleteAttachmentFromFile() error { query := `select href from activitystream where id in (select attachment from activitystream where id=$1)` if err := config.DB.QueryRow(query, obj.Id).Scan(&href); err != nil { - return err + return util.MakeError(err, "DeleteAttachmentFromFile") } href = strings.Replace(href, config.Domain+"/", "", 1) if href != "static/notfound.png" { if _, err := os.Stat(href); err != nil { - return err + return util.MakeError(err, "DeleteAttachmentFromFile") } return os.Remove(href) } @@ -148,13 +99,13 @@ func (obj ObjectBase) DeletePreview() error { query := `delete from activitystream where id=$1` if _, err := config.DB.Exec(query, obj.Id); err != nil { - return err + return util.MakeError(err, "DeletePreview") } query = `delete from cacheactivitystream where id in (select preview from cacheactivitystream where id=$1)` _, err := config.DB.Exec(query, obj.Id) - return err + return util.MakeError(err, "") } func (obj ObjectBase) DeletePreviewFromFile() error { @@ -162,15 +113,13 @@ func (obj ObjectBase) DeletePreviewFromFile() error { query := `select href from activitystream where id in (select preview from activitystream where id=$1)` if err := config.DB.QueryRow(query, obj.Id).Scan(&href); err != nil { - return err + return util.MakeError(err, "DeletePreviewFromFile") } href = strings.Replace(href, config.Domain+"/", "", 1) - if href != "static/notfound.png" { - if _, err := os.Stat(href); err != nil { - return err + return util.MakeError(err, "DeletePreviewFromFile") } return os.Remove(href) } @@ -179,28 +128,28 @@ func (obj ObjectBase) DeletePreviewFromFile() error { } func (obj ObjectBase) DeleteAll() error { - if err := DeleteReportActivity(obj.Id); err != nil { - return err + if err := obj.DeleteReported(); err != nil { + return util.MakeError(err, "DeleteAll") } if err := obj.DeleteAttachmentFromFile(); err != nil { - return err + return util.MakeError(err, "DeleteAll") } if err := obj.DeleteAttachment(); err != nil { - return err + return util.MakeError(err, "DeleteAll") } if err := obj.DeletePreviewFromFile(); err != nil { - return err + return util.MakeError(err, "DeleteAll") } if err := obj.DeletePreview(); err != nil { - return err + return util.MakeError(err, "DeleteAll") } if err := obj.Delete(); err != nil { - return err + return util.MakeError(err, "DeleteAll") } return obj.DeleteRepliedTo() @@ -208,28 +157,32 @@ func (obj ObjectBase) DeleteAll() error { //TODO break this off into seperate for Cache func (obj ObjectBase) Delete() error { - var query = `delete from activitystream where id=$1` - + query := `delete from activitystream where id=$1` if _, err := config.DB.Exec(query, obj.Id); err != nil { - return err + return util.MakeError(err, "Delete") } query = `delete from cacheactivitystream where id=$1` - _, err := config.DB.Exec(query, obj.Id) - return err + return util.MakeError(err, "Delete") } func (obj ObjectBase) DeleteRepliedTo() error { query := `delete from replies where id=$1` _, err := config.DB.Exec(query, obj.Id) - return err + return util.MakeError(err, "DeleteRepliedTo") } func (obj ObjectBase) DeleteInReplyTo() error { query := `delete from replies where id in (select id from replies where inreplyto=$1)` _, err := config.DB.Exec(query, obj.Id) - return err + return util.MakeError(err, "DeleteInReplyTo") +} + +func (obj ObjectBase) DeleteReported() error { + query := `delete from reported where id=$1` + _, err := config.DB.Exec(query, obj.Id) + return util.MakeError(err, "DeleteReported") } func (obj ObjectBase) GetCollection() (Collection, error) { @@ -237,24 +190,21 @@ func (obj ObjectBase) GetCollection() (Collection, error) { req, err := http.NewRequest("GET", obj.Id, nil) if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } req.Header.Set("Accept", config.ActivityStreams) - resp, err := util.RouteProxy(req) if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollection") } 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, util.MakeError(err, "GetCollection") } } } @@ -270,9 +220,8 @@ func (obj ObjectBase) GetCollectionLocal() (Collection, error) { var err error 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` - if rows, err = config.DB.Query(query, obj.Id); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } defer rows.Close() @@ -289,34 +238,35 @@ func (obj ObjectBase) GetCollectionLocal() (Collection, error) { err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive) if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } post.Actor = actor.Id if post.InReplyTo, err = post.GetInReplyTo(); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } var postCnt int var imgCnt int + if post.Replies, postCnt, imgCnt, err = post.GetReplies(); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } if post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesCount(); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } post.Replies.TotalItems = post.Replies.TotalItems + postCnt post.Replies.TotalImgs = post.Replies.TotalImgs + imgCnt if post.Attachment, err = post.Attachment[0].GetAttachment(); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } if post.Preview, err = post.Preview.GetPreview(); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionLocal") } result = append(result, post) @@ -335,18 +285,17 @@ func (obj ObjectBase) GetInReplyTo() ([]ObjectBase, error) { var result []ObjectBase query := `select inreplyto from replies where id =$1` - rows, err := config.DB.Query(query, obj.Id) + if err != nil { - return result, err + return result, util.MakeError(err, "GetInReplyTo") } defer rows.Close() for rows.Next() { var post ObjectBase - if err := rows.Scan(&post.Id); err != nil { - return result, err + return result, util.MakeError(err, "GetInReplyTo") } result = append(result, post) @@ -355,10 +304,11 @@ func (obj ObjectBase) GetInReplyTo() ([]ObjectBase, error) { return result, nil } +//TODO does attachemnts need to be an array in the activitypub structs? func (obj ObjectBase) GetAttachment() ([]ObjectBase, error) { var attachments []ObjectBase - var attachment 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` _ = config.DB.QueryRow(query, obj.Id).Scan(&attachment.Id, &attachment.Type, &attachment.Name, &attachment.Href, &attachment.MediaType, &attachment.Size, &attachment.Published) @@ -382,27 +332,25 @@ func (obj ObjectBase) GetCollectionFromPath() (Collection, error) { var err error 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` - if err = config.DB.QueryRow(query, obj.Id).Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive); err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionFromPath") } post.Actor = actor.Id - var postCnt int - var imgCnt int + post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetReplies() - post.Replies, postCnt, imgCnt, err = post.GetReplies() if err != nil { - return nColl, err + return nColl, util.MakeError(err, "GetCollectionFromPath") } - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - - post.Attachment, _ = post.Attachment[0].GetAttachment() + if post.Attachment, err = post.Attachment[0].GetAttachment(); err != nil { + return nColl, util.MakeError(err, "GetCollectionFromPath") + } - post.Preview, _ = post.Preview.GetPreview() + if post.Preview, err = post.Preview.GetPreview(); err != nil { + return nColl, util.MakeError(err, "GetCollectionFromPath") + } result = append(result, post) @@ -415,58 +363,7 @@ func (obj ObjectBase) GetCollectionFromPath() (Collection, error) { 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) { +func (obj ObjectBase) GetFromPath() (ObjectBase, error) { var post ObjectBase var attch ObjectBase @@ -476,9 +373,10 @@ func GetObjectFromPath(path string) (ObjectBase, error) { post.Preview = &prev query := `select id, name, content, type, published, attributedto, attachment, preview, actor from activitystream where id=$1 order by published desc` - err := config.DB.QueryRow(query, path).Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &post.Actor) + err := config.DB.QueryRow(query, obj.Id).Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &post.Actor) + if err != nil { - return post, err + return post, util.MakeError(err, "GetFromPath") } var nActor Actor @@ -486,64 +384,29 @@ func GetObjectFromPath(path string) (ObjectBase, error) { var postCnt int var imgCnt int + post.Replies, postCnt, imgCnt, err = post.GetReplies() + if err != nil { - return post, err + return post, util.MakeError(err, "GetFromPath") } post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesCount() + if err != nil { - return post, err + return post, util.MakeError(err, "GetFromPath") } post.Replies.TotalItems = post.Replies.TotalItems + postCnt post.Replies.TotalImgs = post.Replies.TotalImgs + imgCnt - post.Attachment, err = post.Attachment[0].GetAttachment() - if err != nil { - return post, err - } - - post.Preview, err = post.Preview.GetPreview() - return post, 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 + return post, util.MakeError(err, "GetFromPath") } - 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 + post.Preview, err = post.Preview.GetPreview() + return post, util.MakeError(err, "GetFromPath") } func (obj NestedObjectBase) GetPreview() (*NestedObjectBase, error) { @@ -562,7 +425,7 @@ func (obj ObjectBase) GetRepliesCount() (int, int, error) { 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` if err := config.DB.QueryRow(query, obj.Id).Scan(&countId, &countImg); err != nil { - return 0, 0, err + return 0, 0, util.MakeError(err, "GetRepliesCount") } return countId, countImg, nil @@ -579,9 +442,8 @@ func (obj ObjectBase) GetReplies() (CollectionBase, int, int, error) { var err error 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` - if rows, err = config.DB.Query(query, obj.Id); err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetReplies") } defer rows.Close() @@ -598,32 +460,29 @@ func (obj ObjectBase) GetReplies() (CollectionBase, int, int, error) { err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive) if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetReplies") } post.InReplyTo = append(post.InReplyTo, obj) post.Actor = actor.Id - var postCnt int - var imgCnt int + post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesReplies() - post.Replies, postCnt, imgCnt, _ = post.GetRepliesReplies() if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetReplies") } - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt - post.Attachment, err = post.Attachment[0].GetAttachment() + if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetReplies") } post.Preview, err = post.Preview.GetPreview() + if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetReplies") } result = append(result, post) @@ -645,9 +504,8 @@ func (obj ObjectBase) GetRepliesLimit(limit int) (CollectionBase, int, int, erro var err error 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` - if rows, err = config.DB.Query(query, obj.Id, limit); err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesLimit") } defer rows.Close() @@ -664,30 +522,32 @@ func (obj ObjectBase) GetRepliesLimit(limit int) (CollectionBase, int, int, erro err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive) if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesLimit") } post.InReplyTo = append(post.InReplyTo, obj) post.Actor = actor.Id - var postCnt int - var imgCnt int + post.Replies, post.Replies.TotalItems, post.Replies.TotalImgs, err = post.GetRepliesReplies() - post.Replies, postCnt, imgCnt, err = post.GetRepliesReplies() if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesLimit") } - post.Replies.TotalItems = postCnt - post.Replies.TotalImgs = imgCnt + post.Attachment, err = post.Attachment[0].GetAttachment() - post.Attachment, _ = post.Attachment[0].GetAttachment() + if err != nil { + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesLimit") + } - post.Preview, _ = post.Preview.GetPreview() + post.Preview, err = post.Preview.GetPreview() - result = append(result, post) + if err != nil { + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesLimit") + } + result = append(result, post) } nColl.OrderedItems = result @@ -708,9 +568,8 @@ func (obj ObjectBase) GetRepliesReplies() (CollectionBase, int, int, error) { var rows *sql.Rows 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` - if rows, err = config.DB.Query(query, obj.Id); err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesReplies") } defer rows.Close() @@ -726,9 +585,8 @@ func (obj ObjectBase) GetRepliesReplies() (CollectionBase, int, int, error) { post.Preview = &prev err = rows.Scan(&postCount, &attachCount, &post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.AttributedTo, &post.Attachment[0].Id, &post.Preview.Id, &actor.Id, &post.TripCode, &post.Sensitive) - if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesReplies") } post.InReplyTo = append(post.InReplyTo, obj) @@ -736,17 +594,18 @@ func (obj ObjectBase) GetRepliesReplies() (CollectionBase, int, int, error) { post.Actor = actor.Id post.Attachment, err = post.Attachment[0].GetAttachment() + if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesReplies") } post.Preview, err = post.Preview.GetPreview() + if err != nil { - return nColl, postCount, attachCount, err + return nColl, postCount, attachCount, util.MakeError(err, "GetRepliesReplies") } result = append(result, post) - } nColl.OrderedItems = result @@ -755,71 +614,22 @@ func (obj ObjectBase) GetRepliesReplies() (CollectionBase, int, int, error) { } func (obj ObjectBase) GetType() (string, error) { - query := `select type from activitystream where id=$1 union select type from cacheactivitystream where id=$1` var nType string + + query := `select type from activitystream where id=$1 union select type from cacheactivitystream where id=$1` if err := config.DB.QueryRow(query, obj.Id).Scan(&nType); err != nil { - return "", err + return "", nil } return nType, nil } -func GetObjectsWithoutPreviewsCallback(callback func(id string, href string, mediatype string, name string, size int, published time.Time) error) error { - var id string - var href string - var mediatype string - var name string - var size int - var published time.Time - - query := `select id, href, mediatype, name, size, published from activitystream where id in (select attachment from activitystream where attachment!='' and preview='')` - if err := config.DB.QueryRow(query).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{} - - if len(to) == 0 { - return nil, nil - } - - 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 (obj ObjectBase) IsCached() (bool, error) { var nID string query := `select id from cacheactivitystream where id=$1` if err := config.DB.QueryRow(query, obj.Id).Scan(&nID); err != nil { - return false, err + return false, util.MakeError(err, "GetType") } return true, nil @@ -830,7 +640,7 @@ func (obj ObjectBase) IsLocal() (bool, error) { query := `select id from activitystream where id=$1` if err := config.DB.QueryRow(query, obj.Id).Scan(&nID); err != nil { - return false, err + return false, util.MakeError(err, "GetType") } return true, nil @@ -840,42 +650,12 @@ func (obj ObjectBase) IsLocal() (bool, error) { func (obj ObjectBase) MarkSensitive(sensitive bool) error { var query = `update activitystream set sensitive=$1 where id=$2` if _, err := config.DB.Exec(query, sensitive, obj.Id); err != nil { - return err + return util.MakeError(err, "MarkSensitive") } query = `update cacheactivitystream set sensitive=$1 where id=$2` _, err := config.DB.Exec(query, sensitive, obj.Id) - return err -} - -func (obj ObjectBase) FromJsonReq(r *http.Request) (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 + return util.MakeError(err, "MarkSensitive") } func (obj ObjectBase) SetAttachmentType(_type string) error { @@ -883,12 +663,12 @@ func (obj ObjectBase) SetAttachmentType(_type string) error { 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, obj.Id); err != nil { - return err + return util.MakeError(err, "SetAttachmentType") } 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, obj.Id) - return err + return util.MakeError(err, "SetAttachmentType") } func (obj ObjectBase) SetAttachmentRepliesType(_type string) error { @@ -896,12 +676,12 @@ func (obj ObjectBase) SetAttachmentRepliesType(_type string) error { 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, obj.Id); err != nil { - return err + return util.MakeError(err, "SetAttachmentRepliesType") } 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, obj.Id) - return err + return util.MakeError(err, "SetAttachmentRepliesType") } func (obj ObjectBase) SetPreviewType(_type string) error { @@ -909,12 +689,12 @@ func (obj ObjectBase) SetPreviewType(_type string) error { 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, obj.Id); err != nil { - return err + return util.MakeError(err, "SetPreviewType") } 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, obj.Id) - return err + return util.MakeError(err, "SetPreviewType") } func (obj ObjectBase) SetPreviewRepliesType(_type string) error { @@ -922,21 +702,21 @@ func (obj ObjectBase) SetPreviewRepliesType(_type string) error { 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, obj.Id); err != nil { - return err + return util.MakeError(err, "SetPreviewRepliesType") } 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, obj.Id) - return err + return util.MakeError(err, "SetPreviewRepliesType") } func (obj ObjectBase) SetType(_type string) error { if err := obj.SetAttachmentType(_type); err != nil { - return err + return util.MakeError(err, "SetType") } if err := obj.SetPreviewType(_type); err != nil { - return err + return util.MakeError(err, "SetType") } return obj._SetType(_type) @@ -944,35 +724,36 @@ func (obj ObjectBase) SetType(_type string) error { func (obj ObjectBase) _SetType(_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, obj.Id); err != nil { - return err + return util.MakeError(err, "_SetType") } query = `update cacheactivitystream set type=$1, deleted=$2 where id=$3` _, err := config.DB.Exec(query, _type, datetime, obj.Id) - return err + return util.MakeError(err, "_SetType") } func (obj ObjectBase) SetRepliesType(_type string) error { if err := obj.SetAttachmentType(_type); err != nil { - return err + return util.MakeError(err, "SetRepliesType") } if err := obj.SetPreviewType(_type); err != nil { - return err + return util.MakeError(err, "SetRepliesType") } if err := obj._SetRepliesType(_type); err != nil { - return err + return util.MakeError(err, "SetRepliesType") } if err := obj.SetAttachmentRepliesType(_type); err != nil { - return err + return util.MakeError(err, "SetRepliesType") } if err := obj.SetPreviewRepliesType(_type); err != nil { - return err + return util.MakeError(err, "SetRepliesType") } return obj.SetType(_type) @@ -983,24 +764,25 @@ func (obj ObjectBase) _SetRepliesType(_type string) error { 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, obj.Id); err != nil { - return err + return util.MakeError(err, "_SetRepliesType") } 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, obj.Id) - return err + return util.MakeError(err, "_SetRepliesType") } func (obj ObjectBase) TombstoneAttachment() 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, obj.Id); err != nil { - return err + return util.MakeError(err, "_SetRepliesType") } 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, obj.Id) - return err + return util.MakeError(err, "_SetRepliesType") } func (obj ObjectBase) TombstoneAttachmentReplies() error { @@ -1008,15 +790,15 @@ func (obj ObjectBase) TombstoneAttachmentReplies() error { query := `select id from activitystream where id in (select id from replies where inreplyto=$1)` if err := config.DB.QueryRow(query, obj.Id).Scan(&attachment.Id); err != nil { - return err + return util.MakeError(err, "TombstoneAttachmentReplies") } if err := attachment.DeleteAttachmentFromFile(); err != nil { - return err + return util.MakeError(err, "TombstoneAttachmentReplies") } if err := attachment.TombstoneAttachment(); err != nil { - return err + return util.MakeError(err, "TombstoneAttachmentReplies") } return nil @@ -1024,14 +806,15 @@ func (obj ObjectBase) TombstoneAttachmentReplies() error { func (obj ObjectBase) TombstonePreview() 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, obj.Id); err != nil { - return err + return util.MakeError(err, "TombstonePreview") } 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, obj.Id) - return err + return util.MakeError(err, "TombstonePreview") } func (obj ObjectBase) TombstonePreviewReplies() error { @@ -1039,39 +822,39 @@ func (obj ObjectBase) TombstonePreviewReplies() error { query := `select id from activitystream where id in (select id from replies where inreplyto=$1)` if err := config.DB.QueryRow(query, obj.Id).Scan(&attachment); err != nil { - return err + return util.MakeError(err, "TombstonePreviewReplies") } if err := attachment.DeletePreviewFromFile(); err != nil { - return err + return util.MakeError(err, "TombstonePreviewReplies") } if err := attachment.TombstonePreview(); err != nil { - return err + return util.MakeError(err, "TombstonePreviewReplies") } return nil } func (obj ObjectBase) Tombstone() error { - if err := DeleteReportActivity(obj.Id); err != nil { - return err + if err := obj.DeleteReported(); err != nil { + return util.MakeError(err, "Tombstone") } if err := obj.DeleteAttachmentFromFile(); err != nil { - return err + return util.MakeError(err, "Tombstone") } if err := obj.TombstoneAttachment(); err != nil { - return err + return util.MakeError(err, "Tombstone") } if err := obj.DeletePreviewFromFile(); err != nil { - return err + return util.MakeError(err, "Tombstone") } if err := obj.TombstonePreview(); err != nil { - return err + return util.MakeError(err, "Tombstone") } return obj._Tombstone() @@ -1079,49 +862,50 @@ func (obj ObjectBase) Tombstone() error { func (obj ObjectBase) _Tombstone() 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, obj.Id); err != nil { - return err + return util.MakeError(err, "_Tombstone") } query = `update cacheactivitystream set type='Tombstone', name='', content='', attributedto='deleted', tripcode='', deleted=$1 where id=$2` _, err := config.DB.Exec(query, datetime, obj.Id) - return err + return util.MakeError(err, "_Tombstone") } func (obj ObjectBase) TombstoneReplies() error { - if err := DeleteReportActivity(obj.Id); err != nil { - return err + if err := obj.DeleteReported(); err != nil { + return util.MakeError(err, "TombstoneReplies") } if err := obj.DeleteAttachmentFromFile(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } if err := obj.TombstoneAttachment(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } if err := obj.DeletePreviewFromFile(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } if err := obj.TombstonePreview(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } if err := obj._TombstoneReplies(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } if err := obj.TombstoneAttachmentReplies(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } if err := obj.TombstonePreviewReplies(); err != nil { - return err + return util.MakeError(err, "TombstoneReplies") } return nil @@ -1129,31 +913,182 @@ func (obj ObjectBase) TombstoneReplies() error { func (obj ObjectBase) _TombstoneReplies() 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, obj.Id); err != nil { - return err + return util.MakeError(err, "_TombstoneReplies") } 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, obj.Id) - return err + return util.MakeError(err, "_TombstoneReplies") } func (obj ObjectBase) UpdateType(_type string) error { query := `update activitystream set type=$2 where id=$1 and type !='Tombstone'` if _, err := config.DB.Exec(query, obj.Id, _type); err != nil { - return err + return util.MakeError(err, "UpdateType") } query = `update cacheactivitystream set type=$2 where id=$1 and type !='Tombstone'` _, err := config.DB.Exec(query, obj.Id, _type) - return err + return util.MakeError(err, "UpdateType") } func (obj ObjectBase) UpdatePreview(preview string) error { query := `update activitystream set preview=$1 where attachment=$2` _, err := config.DB.Exec(query, preview, obj.Id) - return err + return util.MakeError(err, "UpdatePreview") +} + +func (obj ObjectBase) Write() (ObjectBase, error) { + id, err := util.CreateUniqueID(obj.Actor) + if err != nil { + return obj, util.MakeError(err, "Write") + } + + 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, util.MakeError(err, "Write") + } + + 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 := obj.Preview.WritePreview(); err != nil { + return obj, util.MakeError(err, "Write") + } + } + for i := range obj.Attachment { + id, err := util.CreateUniqueID(obj.Actor) + if err != nil { + return obj, util.MakeError(err, "Write") + } + + 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 + obj.Attachment[i].WriteAttachment() + obj.WriteWithAttachment(obj.Attachment[i]) + } + } else { + if err := obj._Write(); err != nil { + return obj, util.MakeError(err, "Write") + } + } + + if err := obj.WriteReply(); err != nil { + return obj, util.MakeError(err, "Write") + } + + err = obj.WriteWallet() + return obj, util.MakeError(err, "Write") +} + +func (obj ObjectBase) _Write() 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 util.MakeError(err, "_Write") +} + +func (obj ObjectBase) WriteAttachment() 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 util.MakeError(err, "WriteAttachment") +} + +func (obj ObjectBase) WriteAttachmentCache() error { + var id string + + query := `select id from cacheactivitystream where id=$1` + if err := config.DB.QueryRow(query, obj.Id).Scan(&id); err != nil { + 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 util.MakeError(err, "WriteAttachmentCache") + } + + return nil +} + +func (obj ObjectBase) WriteCache() error { + var id string + + 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` + if err := config.DB.QueryRow(query, obj.Id).Scan(&id); err != nil { + 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 util.MakeError(err, "WriteCache") + } + + return nil +} + +func (obj ObjectBase) WriteCacheWithAttachment(attachment ObjectBase) error { + var id string + + 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` + if err := config.DB.QueryRow(query, obj.Id).Scan(&id); err != nil { + 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, obj.Preview.Id, obj.Published, obj.Updated, obj.AttributedTo, obj.Actor, obj.TripCode, obj.Sensitive) + return util.MakeError(err, "WriteCacheWithAttachment") + } + + return nil +} + +func (obj NestedObjectBase) WritePreview() 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 util.MakeError(err, "WritePreview") +} + +func (obj NestedObjectBase) WritePreviewCache() error { + var id string + + query := `select id from cacheactivitystream where id=$1` + err := config.DB.QueryRow(query, obj.Id).Scan(&id) + if err != nil { + 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 util.MakeError(err, "WritePreviewCache") + } + + return nil } func (obj ObjectBase) WriteReply() error { @@ -1161,35 +1096,34 @@ func (obj ObjectBase) WriteReply() error { if isOP, err := obj.CheckIfOP(); err == nil && !isOP && i == 0 { var nObj ObjectBase nObj.Id = e.Id + nType, err := nObj.GetType() if err != nil { - return err + return util.MakeError(err, "WriteReply") } if nType == "Archive" { if err := obj.UpdateType("Archive"); err != nil { - return err + return util.MakeError(err, "WriteReply") } } } else if err != nil { - return err + return util.MakeError(err, "WriteReply") } - query := `select id from replies where id=$1 and inreplyto=$2` - var id string + + query := `select id from replies where id=$1 and inreplyto=$2` if err := config.DB.QueryRow(query, obj.Id, e.Id).Scan(&id); err != nil { query := `insert into replies (id, inreplyto) values ($1, $2)` - - _, err := config.DB.Exec(query, obj.Id, e.Id) - if err != nil { - return err + if _, err := config.DB.Exec(query, obj.Id, e.Id); err != nil { + return util.MakeError(err, "WriteReply") } } update := true - for _, e := range obj.Option { - if e == "sage" || e == "nokosage" { + for _, o := range obj.Option { + if o == "sage" || o == "nokosage" { update = false break } @@ -1197,20 +1131,19 @@ func (obj ObjectBase) WriteReply() error { if update { if err := e.WriteUpdate(); err != nil { - return err + return util.MakeError(err, "WriteReply") } } } - if len(obj.InReplyTo) < 1 { - query := `select id from replies where id=$1 and inreplyto=$2` - + if len(obj.InReplyTo) == 0 { var id string - if err := config.DB.QueryRow(query, obj.Id, "").Scan(&id); err != nil { - query := `insert into replies (id, inreplyto) values ($1, $2)` + query := `select id from replies where id=$1 and inreplyto=''` + if err := config.DB.QueryRow(query, obj.Id).Scan(&id); err != nil { + query := `insert into replies (id, inreplyto) values ($1, $2)` if _, err := config.DB.Exec(query, obj.Id, ""); err != nil { - return err + return util.MakeError(err, "WriteReply") } } } @@ -1219,42 +1152,25 @@ func (obj ObjectBase) WriteReply() error { } func (obj ObjectBase) WriteReplyLocal(replyto string) error { - query := `select id from replies where id=$1 and inreplyto=$2` - rows, err := config.DB.Query(query, obj.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)` + query := `select id from replies where id=$1 and inreplyto=$2` + if err := config.DB.QueryRow(query, obj.Id, replyto).Scan(&nID); err != nil { + query := `insert into replies (id, inreplyto) values ($1, $2)` if _, err := config.DB.Exec(query, obj.Id, replyto); err != nil { - return err + return util.MakeError(err, "WriteReplyLocal") } } - query = `select inreplyto from replies where id=$1` + var val string - rows, err = config.DB.Query(query, replyto) - if err != nil { - return err - } - defer rows.Close() + query = `select inreplyto from replies where id=$1` + if err := config.DB.QueryRow(query, replyto).Scan(&val); err != nil { + updated := time.Now().UTC().Format(time.RFC3339) + query := `update activitystream set updated=$1 where id=$2` - 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 - } + if _, err := config.DB.Exec(query, updated, replyto); err != nil { + return util.MakeError(err, "WriteReplyLocal") } } @@ -1262,17 +1178,16 @@ func (obj ObjectBase) WriteReplyLocal(replyto string) error { } func (obj ObjectBase) WriteObjectToCache() (ObjectBase, error) { - if res, err := util.IsPostBlacklist(obj.Content); err == nil && res { + if isBlacklisted, err := util.IsPostBlacklist(obj.Content); err != nil || isBlacklisted { fmt.Println("\n\nBlacklist post blocked\n\n") - return obj, nil - } else { - return obj, err + return obj, util.MakeError(err, "WriteObjectToCache") } if len(obj.Attachment) > 0 { if obj.Preview.Href != "" { obj.Preview.WritePreviewCache() } + for i, _ := range obj.Attachment { obj.Attachment[i].WriteAttachmentCache() obj.WriteCacheWithAttachment(obj.Attachment[i]) @@ -1282,6 +1197,7 @@ func (obj ObjectBase) WriteObjectToCache() (ObjectBase, error) { } obj.WriteReply() + if obj.Replies.OrderedItems != nil { for _, e := range obj.Replies.OrderedItems { e.WriteCache() @@ -1291,89 +1207,43 @@ func (obj ObjectBase) WriteObjectToCache() (ObjectBase, error) { return obj, nil } -func (obj ObjectBase) Write() (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 := obj.Preview.WritePreview(); 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 - obj.Attachment[i].WriteAttachment() - obj.WriteWithAttachment(obj.Attachment[i]) - } - - } else { - if err := obj._Write(); err != nil { - return obj, err - } - } - - if err := obj.WriteReply(); err != nil { - return obj, err - } - - err = obj.WriteWallet() - return obj, err -} - -func (obj ObjectBase) _Write() 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 (obj ObjectBase) WriteUpdate() 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 + return util.MakeError(err, "WriteUpdate") } query = `update cacheactivitystream set updated=$1 where id=$2` _, err := config.DB.Exec(query, time.Now().UTC().Format(time.RFC3339), obj.Id) - return err + return util.MakeError(err, "WriteUpdate") } func (obj ObjectBase) WriteWallet() error { for _, e := range obj.Option { if e == "wallet" { - for _, e := range obj.Wallet { + for _, w := 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 + if _, err := config.DB.Exec(query, obj.Id, w.Type, w.Address); err != nil { + return util.MakeError(err, "WriteWallet") } } return nil } } + return nil } + +func (obj ObjectBase) WriteWithAttachment(attachment ObjectBase) { + 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, obj.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) + } +} diff --git a/activitypub/pem.go b/activitypub/pem.go index ab225a9..408be0c 100644 --- a/activitypub/pem.go +++ b/activitypub/pem.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/FChannel0/FChannel-Server/config" + "github.com/FChannel0/FChannel-Server/util" ) type Signature struct { @@ -25,7 +26,7 @@ type Signature struct { func CreatePem(actor Actor) error { privatekey, err := rsa.GenerateKey(crand.Reader, 2048) if err != nil { - return err + return util.MakeError(err, "CreatePem") } privateKeyBytes := x509.MarshalPKCS1PrivateKey(privatekey) @@ -37,17 +38,17 @@ func CreatePem(actor Actor) error { privatePem, err := os.Create("./pem/board/" + actor.Name + "-private.pem") if err != nil { - return err + return util.MakeError(err, "CreatePem") } if err := pem.Encode(privatePem, privateKeyBlock); err != nil { - return err + return util.MakeError(err, "CreatePem") } publickey := &privatekey.PublicKey publicKeyBytes, err := x509.MarshalPKIXPublicKey(publickey) if err != nil { - return err + return util.MakeError(err, "CreatePem") } publicKeyBlock := &pem.Block{ @@ -57,16 +58,16 @@ func CreatePem(actor Actor) error { publicPem, err := os.Create("./pem/board/" + actor.Name + "-public.pem") if err != nil { - return err + return util.MakeError(err, "CreatePem") } if err := pem.Encode(publicPem, publicKeyBlock); err != nil { - return err + return util.MakeError(err, "CreatePem") } _, err = os.Stat("./pem/board/" + actor.Name + "-public.pem") if os.IsNotExist(err) { - return err + return util.MakeError(err, "CreatePem") } else { return StorePemToDB(actor) } @@ -81,7 +82,7 @@ so DO NOT LOSE IT!!! If you lose it, YOU WILL LOSE ACCESS TO YOUR BOARD!`) func CreatePublicKeyFromPrivate(actor *Actor, publicKeyPem string) error { publicFilename, err := GetActorPemFileFromDB(publicKeyPem) if err != nil { - return err + return util.MakeError(err, "CreatePublicKeyFromPrivate") } privateFilename := strings.ReplaceAll(publicFilename, "public.pem", "private.pem") @@ -89,7 +90,7 @@ func CreatePublicKeyFromPrivate(actor *Actor, publicKeyPem string) error { // Not a lost cause priv, err := ioutil.ReadFile(privateFilename) if err != nil { - return err + return util.MakeError(err, "CreatePublicKeyFromPrivate") } block, _ := pem.Decode([]byte(priv)) @@ -99,12 +100,12 @@ func CreatePublicKeyFromPrivate(actor *Actor, publicKeyPem string) error { key, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { - return err + return util.MakeError(err, "CreatePublicKeyFromPrivate") } publicKeyDer, err := x509.MarshalPKIXPublicKey(&key.PublicKey) if err != nil { - return err + return util.MakeError(err, "CreatePublicKeyFromPrivate") } pubKeyBlock := pem.Block{ @@ -115,11 +116,11 @@ func CreatePublicKeyFromPrivate(actor *Actor, publicKeyPem string) error { publicFileWriter, err := os.Create(publicFilename) if err != nil { - return err + return util.MakeError(err, "CreatePublicKeyFromPrivate") } if err := pem.Encode(publicFileWriter, &pubKeyBlock); err != nil { - return err + return util.MakeError(err, "CreatePublicKeyFromPrivate") } } else { fmt.Println(`\nUnable to locate private key from public key generation. Now, @@ -140,14 +141,14 @@ func GetActorPemFromDB(pemID string) (PublicKeyPem, error) { query := `select id, owner, file from publickeypem where id=$1` if err := config.DB.QueryRow(query, pemID).Scan(&pem.Id, &pem.Owner, &pem.PublicKeyPem); err != nil { - return pem, err + return pem, util.MakeError(err, "GetActorPemFromDB") } dir, _ := os.Getwd() dir = dir + "" + strings.Replace(pem.PublicKeyPem, ".", "", 1) f, err := os.ReadFile(dir) if err != nil { - return pem, err + return pem, util.MakeError(err, "GetActorPemFromDB") } pem.PublicKeyPem = strings.ReplaceAll(string(f), "\r\n", `\n`) @@ -159,7 +160,7 @@ 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 + return "", util.MakeError(err, "GetActorPemFileFromDB") } defer rows.Close() @@ -175,7 +176,7 @@ 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 + return util.MakeError(err, "StorePemToDB") } defer rows.Close() @@ -191,13 +192,13 @@ func StorePemToDB(actor Actor) error { 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 + return util.MakeError(err, "StorePemToDB") } 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 + return util.MakeError(err, "StorePemToDB") } func ParseHeaderSignature(signature string) Signature { diff --git a/activitypub/util.go b/activitypub/util.go new file mode 100644 index 0000000..7aee666 --- /dev/null +++ b/activitypub/util.go @@ -0,0 +1,489 @@ +package activitypub + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "mime/multipart" + "net/http" + "os" + "regexp" + "strings" + "time" + + "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 CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ([]ObjectBase, *os.File, error) { + contentType, err := util.GetFileContentType(file) + if err != nil { + return nil, nil, util.MakeError(err, "CreateAttachmentObject") + } + + 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, util.MakeError(err, "CreateAttachmentObject") + } + + 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 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 CreateObject(objType string) ObjectBase { + var nObj ObjectBase + + nObj.Type = objType + nObj.Published = time.Now().UTC() + nObj.Updated = time.Now().UTC() + + return nObj +} + +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, util.MakeError(err, "GetActivityFromJson") + } + + 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, util.MakeError(err, "GetActivityFromJson") + } + + nType = "Create" + } else { + jObj, err = GetObjectFromJson(respActivity.ObjectRaw) + if err != nil { + return nActivity, util.MakeError(err, "GetActivityFromJson") + } + + nType = respActivity.Type + } + + actor, err := GetActorFromJson(respActivity.ActorRaw) + if err != nil { + return nActivity, util.MakeError(err, "GetActivityFromJson") + } + + to, err := GetToFromJson(respActivity.ToRaw) + if err != nil { + return nActivity, util.MakeError(err, "GetActivityFromJson") + } + + cc, err := GetToFromJson(respActivity.CcRaw) + if err != nil { + return nActivity, util.MakeError(err, "GetActivityFromJson") + } + + 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, util.MakeError(err, "GetActivityFromJson") + } + + return nActivity, nil +} + +func GetObjectFromJson(obj []byte) (ObjectBase, error) { + var generic interface{} + var nObj ObjectBase + + if err := json.Unmarshal(obj, &generic); err != nil { + return ObjectBase{}, util.MakeError(err, "GetObjectFromJson") + } + + 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, util.MakeError(err, "GetObjectFromJson") + } + + 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, util.MakeError(err, "GetObjectFromJson") + } + + nObj = *arrContext.Object + break + + case string: + var lObj ObjectBase + var arrContext ObjectString + + if err := json.Unmarshal(obj, &arrContext.Object); err != nil { + return nObj, util.MakeError(err, "GetObjectFromJson") + } + + lObj.Id = arrContext.Object + nObj = lObj + break + } + } + + return nObj, nil +} + +func GetObjectsWithoutPreviewsCallback(callback func(id string, href string, mediatype string, name string, size int, published time.Time) error) error { + var id string + var href string + var mediatype string + var name string + var size int + var published time.Time + + query := `select id, href, mediatype, name, size, published from activitystream where id in (select attachment from activitystream where attachment!='' and preview='')` + if err := config.DB.QueryRow(query).Scan(&id, &href, &mediatype, &name, &size, &published); err != nil { + return util.MakeError(err, "GetObjectsWithoutPreviewsCallback") + } + + if err := callback(id, href, mediatype, name, size, published); err != nil { + return util.MakeError(err, "GetObjectsWithoutPreviewsCallback") + } + + return nil +} + +func HasContextFromJson(context []byte) (bool, error) { + var generic interface{} + + err := json.Unmarshal(context, &generic) + if err != nil { + return false, util.MakeError(err, "GetObjectsWithoutPreviewsCallback") + } + + 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, util.MakeError(err, "GetObjectsWithoutPreviewsCallback") +} + +func GetActorByNameFromDB(name string) (Actor, error) { + var nActor Actor + var publicKeyPem string + + query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where name=$1` + err := config.DB.QueryRow(query, name).Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) + + if err != nil { + return nActor, util.MakeError(err, "GetActorByNameFromDB") + } + + if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { + if err := CreatePublicKeyFromPrivate(&nActor, publicKeyPem); err != nil { + return nActor, util.MakeError(err, "GetActorByNameFromDB") + } + } + + return nActor, nil +} + +func GetActorCollectionReq(collection string) (Collection, error) { + var nCollection Collection + + req, err := http.NewRequest("GET", collection, nil) + + if err != nil { + return nCollection, util.MakeError(err, "GetActorCollectionReq") + } + + // 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, util.MakeError(err, "GetActorCollectionReq") + } + + defer resp.Body.Close() + if resp.StatusCode == 200 { + body, _ := ioutil.ReadAll(resp.Body) + + if err := json.Unmarshal(body, &nCollection); err != nil { + return nCollection, util.MakeError(err, "GetActorCollectionReq") + } + } + + return nCollection, 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 GetActorFromDB(id string) (Actor, error) { + var nActor Actor + var publicKeyPem string + + query := `select type, id, name, preferedusername, inbox, outbox, following, followers, restricted, summary, publickeypem from actor where id=$1` + err := config.DB.QueryRow(query, id).Scan(&nActor.Type, &nActor.Id, &nActor.Name, &nActor.PreferredUsername, &nActor.Inbox, &nActor.Outbox, &nActor.Following, &nActor.Followers, &nActor.Restricted, &nActor.Summary, &publicKeyPem) + + if err != nil { + return nActor, util.MakeError(err, "GetActorFromDB") + } + + nActor.PublicKey, err = GetActorPemFromDB(publicKeyPem) + + if err != nil { + return nActor, util.MakeError(err, "GetActorFromDB") + } + + if nActor.Id != "" && nActor.PublicKey.PublicKeyPem == "" { + if err := CreatePublicKeyFromPrivate(&nActor, publicKeyPem); err != nil { + return nActor, util.MakeError(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, util.MakeError(err, "GetActorFromJson") + } + + 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, util.MakeError(err, "GetActorFromJson") + } + + return nActor, nil +} + +func GetActorsFollowPostFromId(actors []string, id string) (Collection, error) { + var collection Collection + + for _, e := range actors { + obj := ObjectBase{Id: e + "/" + id} + tempCol, err := obj.GetCollectionFromPath() + if err != nil { + return collection, util.MakeError(err, "GetActorsFollowPostFromId") + } + + if len(tempCol.OrderedItems) > 0 { + collection = tempCol + return collection, nil + } + } + + return collection, 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, util.MakeError(err, "GetBoards") + } + + 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, util.MakeError(err, "GetBoards") + } + + boards = append(boards, *actor) + } + + return boards, nil +} + +func GetToFromJson(to []byte) ([]string, error) { + var generic interface{} + + if len(to) == 0 { + return nil, nil + } + + err := json.Unmarshal(to, &generic) + if err != nil { + return nil, util.MakeError(err, "GetToFromJson") + } + + 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, util.MakeError(err, "GetToFromJson") + } + + return nil, nil +} + +func GetActorAndInstance(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 "", "" +} -- cgit v1.2.3