From a9bed9fea9fa90f95309738acd3b60f45d860fa4 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Tue, 17 Mar 2020 13:13:05 -0400 Subject: [PATCH 1/6] Prevent nil pointer panic from ActivityObject() method Previously, we might potentially return a nil activitystreams.Object, which would crash the app. This fixes that. --- posts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posts.go b/posts.go index 35e9bd3..5d9b9f8 100644 --- a/posts.go +++ b/posts.go @@ -1182,7 +1182,7 @@ func (p *PublicPost) ActivityObject(app *App) *activitystreams.Object { actorIRI, err := app.db.GetProfilePageFromHandle(app, handle) if err != nil { log.Info("Can't find this user either in the database nor in the remote instance") - return nil + continue } mentionedUsers[handle] = actorIRI } From 471a9e06026f6597ea3e4715671e5cc0297a09f4 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Tue, 17 Mar 2020 13:42:09 -0400 Subject: [PATCH 2/6] Store AP handles consistently This ensures handles are always stored without leading @ symbol. --- database.go | 1 + 1 file changed, 1 insertion(+) diff --git a/database.go b/database.go index f5e4564..19476e8 100644 --- a/database.go +++ b/database.go @@ -2605,6 +2605,7 @@ func handleFailedPostInsert(err error) error { } func (db *datastore) GetProfilePageFromHandle(app *App, handle string) (string, error) { + handle = strings.TrimLeft(handle, "@") actorIRI := "" remoteUser, err := getRemoteUserFromHandle(app, handle) if err != nil { From 97aec9c158de685478662e14945a6326b18ebc53 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Tue, 17 Mar 2020 13:42:51 -0400 Subject: [PATCH 3/6] Fix error / info logging around AP mentions This fixes log formatting and makes verbiage consistent & concise. --- database.go | 6 +++--- posts.go | 2 +- webfinger.go | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/database.go b/database.go index 19476e8..bd6b5d6 100644 --- a/database.go +++ b/database.go @@ -2618,21 +2618,21 @@ func (db *datastore) GetProfilePageFromHandle(app *App, handle string) (string, if errRemoteUser == nil { _, err := app.db.Exec("UPDATE remoteusers SET handle = ? WHERE actor_id = ?", handle, actorIRI) if err != nil { - log.Error("Can't update handle (" + handle + ") in database for user " + actorIRI) + log.Error("Couldn't update handle '%s' for user %s", handle, actorIRI) } } else { // this probably means we don't have the user in the table so let's try to insert it // here we need to ask the server for the inboxes remoteActor, err := activityserve.NewRemoteActor(actorIRI) if err != nil { - log.Error("Couldn't fetch remote actor", err) + log.Error("Couldn't fetch remote actor: %v", err) } if debugging { log.Info("%s %s %s %s", actorIRI, remoteActor.GetInbox(), remoteActor.GetSharedInbox(), handle) } _, err = app.db.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox, handle) VALUES(?, ?, ?, ?)", actorIRI, remoteActor.GetInbox(), remoteActor.GetSharedInbox(), handle) if err != nil { - log.Error("Can't insert remote user in database", err) + log.Error("Couldn't insert remote user: %v", err) return "", err } } diff --git a/posts.go b/posts.go index 5d9b9f8..c632ba9 100644 --- a/posts.go +++ b/posts.go @@ -1181,7 +1181,7 @@ func (p *PublicPost) ActivityObject(app *App) *activitystreams.Object { for _, handle := range mentions { actorIRI, err := app.db.GetProfilePageFromHandle(app, handle) if err != nil { - log.Info("Can't find this user either in the database nor in the remote instance") + log.Info("Couldn't find this user locally or remotely") continue } mentionedUsers[handle] = actorIRI diff --git a/webfinger.go b/webfinger.go index 61f6867..993272f 100644 --- a/webfinger.go +++ b/webfinger.go @@ -101,20 +101,20 @@ func RemoteLookup(handle string) string { parts := strings.Split(handle, "@") resp, err := http.Get("https://" + parts[1] + "/.well-known/webfinger?resource=acct:" + handle) if err != nil { - log.Error("Error performing webfinger request", err) + log.Error("Error on webfinger request: %v", err) return "" } body, err := ioutil.ReadAll(resp.Body) if err != nil { - log.Error("Error reading webfinger response", err) + log.Error("Error on webfinger response: %v", err) return "" } var result webfinger.Resource err = json.Unmarshal(body, &result) if err != nil { - log.Error("Unsupported webfinger response received: %v", err) + log.Error("Unable to parse webfinger response: %v", err) return "" } From ac522ed600f52ee33564a6fcc94f5da6e81e33cc Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Tue, 17 Mar 2020 13:43:25 -0400 Subject: [PATCH 4/6] Reuse mention regex This makes the app less error-prone by avoiding a regexp.MustCompile() call in the ActivityObject() method, saves CPU work, and reuses code. --- posts.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/posts.go b/posts.go index c632ba9..15176b7 100644 --- a/posts.go +++ b/posts.go @@ -1175,8 +1175,7 @@ func (p *PublicPost) ActivityObject(app *App) *activitystreams.Object { stripper := bluemonday.StrictPolicy() content := stripper.Sanitize(p.Content) - mentionRegex := regexp.MustCompile(`@[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\b`) - mentions := mentionRegex.FindAllString(content, -1) + mentions := mentionReg.FindAllString(content, -1) for _, handle := range mentions { actorIRI, err := app.db.GetProfilePageFromHandle(app, handle) From f9cd87ae3a3fbc10af9e46ad768e60df9cb3fc17 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Thu, 19 Mar 2020 13:43:07 -0400 Subject: [PATCH 5/6] Log handle on GetProfilePageFromHandle err --- posts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posts.go b/posts.go index 15176b7..82907ba 100644 --- a/posts.go +++ b/posts.go @@ -1180,7 +1180,7 @@ func (p *PublicPost) ActivityObject(app *App) *activitystreams.Object { for _, handle := range mentions { actorIRI, err := app.db.GetProfilePageFromHandle(app, handle) if err != nil { - log.Info("Couldn't find this user locally or remotely") + log.Info("Couldn't find user '%s' locally or remotely", handle) continue } mentionedUsers[handle] = actorIRI From 9dbba9d8c7eca98461532fbfaecc2bd93db3e4b1 Mon Sep 17 00:00:00 2001 From: Matt Baer Date: Tue, 24 Mar 2020 07:59:00 -0400 Subject: [PATCH 6/6] Make `handle` column in remoteusers NULL This alters the V6 migration to make the column NULLable. Anyone who has already run this migration will need to manually update their database. --- activitypub.go | 5 ++++- migrations/v6.go | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/activitypub.go b/activitypub.go index c3df29f..d2980ff 100644 --- a/activitypub.go +++ b/activitypub.go @@ -708,7 +708,8 @@ func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error { func getRemoteUser(app *App, actorID string) (*RemoteUser, error) { u := RemoteUser{ActorID: actorID} - err := app.db.QueryRow("SELECT id, inbox, shared_inbox, handle FROM remoteusers WHERE actor_id = ?", actorID).Scan(&u.ID, &u.Inbox, &u.SharedInbox, &u.Handle) + var handle sql.NullString + err := app.db.QueryRow("SELECT id, inbox, shared_inbox, handle FROM remoteusers WHERE actor_id = ?", actorID).Scan(&u.ID, &u.Inbox, &u.SharedInbox, &handle) switch { case err == sql.ErrNoRows: return nil, impart.HTTPError{http.StatusNotFound, "No remote user with that ID."} @@ -717,6 +718,8 @@ func getRemoteUser(app *App, actorID string) (*RemoteUser, error) { return nil, err } + u.Handle = handle.String + return &u, nil } diff --git a/migrations/v6.go b/migrations/v6.go index c6f5012..8e0be78 100644 --- a/migrations/v6.go +++ b/migrations/v6.go @@ -1,5 +1,5 @@ /* - * Copyright © 2019 A Bunch Tell LLC. + * Copyright © 2019-2020 A Bunch Tell LLC. * * This file is part of WriteFreely. * @@ -13,7 +13,7 @@ package migrations func supportActivityPubMentions(db *datastore) error { t, err := db.Begin() - _, err = t.Exec(`ALTER TABLE remoteusers ADD COLUMN handle ` + db.typeVarChar(255) + ` DEFAULT '' NOT NULL`) + _, err = t.Exec(`ALTER TABLE remoteusers ADD COLUMN handle ` + db.typeVarChar(255) + ` NULL`) if err != nil { t.Rollback() return err