master
Michael Demetriou 2019-10-08 15:23:25 +03:00
parent 76f9e14b93
commit 5fc3b48e70
4 changed files with 64 additions and 37 deletions

19
TODO
View File

@ -13,7 +13,7 @@
[✔] Handle being followed
[✔] When followed, the handler should write the new follower to file
[✔] Make sure we send our boosts to all our followers
[ ] Write incoming activities to disk (do we have to?)
[x] Write incoming activities to disk (do we have to?)
[✔] Write all the announcements (boosts) to the database to
their correct actors
[✔] Check if we are already following users
@ -22,12 +22,13 @@
[✔] Make OS-independent (mosty directory separators)
[✔] Create outbox.json programmatically
[✔] Make storage configurable (search for "storage" in project)
[ ] Check if we're boosting only stuff from actors we follow, not whatever comes
[] Check if we're boosting only stuff from actors we follow, not whatever comes
through in our inbox
[✔] Boost not only articles but other things too
[✔] Sanitize input, never allow slashes or dots
[✔] Add summary to actors.json
[ ] Check local actor names for characters illegal for filenames and ban them
[✔] Check local actor names for characters illegal for filenames and ban them
(Done in pherephone, not activityserve)
[✔] Create debug flag
[✔] Write to following only upon accept
(waiting to actually get an accept so that I can test this)
@ -41,16 +42,16 @@
[ ] Implement nodeinfo and statistics
[✔] Accept even if already follows us
[✔] Handle paging
[ ] Test paging
[] Test paging
[✔] Handle http signatures
[ ] Verify http signatures
[ ] Refactor, comment and clean up
[ ] Split to pherephone and activityServe
[] Refactor, comment and clean up
[] Split to pherephone and activityServe
[ ] Decide what's to be done with actors removed from `actors.json`.
[ ] Remove them?
[ ] Leave them read-only?
[ ] Leave them as is?
[] Leave them as is?
[✔] Handle followers and following uri's
[ ] Do I care about the inbox?
[ ] Expose configuration to apps
[ ] Do not boost replies (configurable)
[] Expose configuration to apps
[] Do not boost replies (configurable)

View File

@ -54,8 +54,8 @@ type ActorToSave struct {
Followers, Following, Rejected, Requested map[string]interface{}
}
// MakeActor returns a new local actor we can act
// on behalf of
// MakeActor creates and returns a new local actor we can act
// on behalf of. It also creates its files on disk
func MakeActor(name, summary, actorType string) (Actor, error) {
followers := make(map[string]interface{})
following := make(map[string]interface{})
@ -125,7 +125,7 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
return actor, nil
}
// GetOutboxIRI returns the outbox iri in net/url
// GetOutboxIRI returns the outbox iri in net/url format
func (a *Actor) GetOutboxIRI() *url.URL {
iri := a.iri + "/outbox"
nuiri, _ := url.Parse(iri)
@ -133,7 +133,8 @@ func (a *Actor) GetOutboxIRI() *url.URL {
}
// LoadActor searches the filesystem and creates an Actor
// from the data in name.json
// from the data in <name>.json
// This does not preserve events so use with caution
func LoadActor(name string) (Actor, error) {
// make sure our users can't read our hard drive
if strings.ContainsAny(name, "./ ") {
@ -161,9 +162,6 @@ func LoadActor(name string) (Actor, error) {
return Actor{}, err
}
// publicKeyNewLines := strings.ReplaceAll(jsonData["PublicKey"].(string), "\\n", "\n")
// privateKeyNewLines := strings.ReplaceAll(jsonData["PrivateKey"].(string), "\\n", "\n")
publicKeyDecoded, rest := pem.Decode([]byte(jsonData["PublicKey"].(string)))
if publicKeyDecoded == nil {
log.Info(rest)
@ -245,7 +243,7 @@ func GetActor(name, summary, actorType string) (Actor, error) {
}
// func LoadActorFromIRI(iri string) a Actor{
// TODO, this should parse the iri and load the right actor
// }
// save the actor to file
@ -445,6 +443,9 @@ func (a *Actor) GetFollowing(page int) (response []byte, err error) {
return a.getPeers(page, "following")
}
// signedHTTPPost performs an HTTP post on behalf of Actor with the
// request-target, date, host and digest headers signed
// with the actor's private key.
func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err error) {
b, err := json.Marshal(content)
if err != nil {
@ -498,7 +499,7 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
return
}
responseData, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("POST request to %s succeeded (%d): %s \nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatJSON(byteCopy), FormatHeaders(req.Header))
log.Errorf("POST request to %s succeeded (%d): %s \nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatJSON(byteCopy), FormatHeaders(req.Header))
return
}
@ -556,6 +557,8 @@ func (a *Actor) NewFollower(iri string, inbox string) error {
return a.save()
}
// appendToOutbox adds a new line with the id of the activity
// to outbox.txt
func (a *Actor) appendToOutbox(iri string) (err error) {
// create outbox file if it doesn't exist
var outbox *os.File
@ -574,6 +577,7 @@ func (a *Actor) appendToOutbox(iri string) (err error) {
return nil
}
// batchSend sends a batch of http posts to a list of recipients
func (a *Actor) batchSend(activity map[string]interface{}, recipients []string) (err error) {
for _, v := range recipients {
err := a.signedHTTPPost(activity, v)
@ -584,6 +588,7 @@ func (a *Actor) batchSend(activity map[string]interface{}, recipients []string)
return
}
// send to followers sends a batch of http posts to each one of the followers
func (a *Actor) sendToFollowers(activity map[string]interface{}) (err error) {
recipients := make([]string, len(a.followers))
@ -645,15 +650,34 @@ func (a *Actor) Follow(user string) (err error) {
// was accepted when initially following that user
// (this is read from the `actor.following` map
func (a *Actor) Unfollow(user string) {
// if we have a request to follow this user cancel it
cancelRequest := false
if _, ok := a.requested[user]; ok {
log.Info("Cancelling follow request")
cancelRequest = true
// then continue to send the unfollow to the receipient
// to inform them that the request is cancelled.
} else if _, ok := a.following[user]; !ok {
log.Info("We are not following this user, ignoring...")
return
}
log.Info("Unfollowing " + user)
var hash string
// find the id of the original follow
if cancelRequest {
hash = a.requested[user].(string)
} else {
hash = a.following[user].(string)
}
// create an undo activiy
undo := make(map[string]interface{})
undo["@context"] = context()
undo["actor"] = a.iri
// find the id of the original follow
hash := a.following[user].(string)
undo["id"] = baseURL + "/item/" + hash + "/undo"
undo["type"] = "Undo"
follow := make(map[string]interface{})
@ -673,8 +697,6 @@ func (a *Actor) Unfollow(user string) {
return
}
// only if we're already following them
if _, ok := a.following[user]; ok {
PrettyPrint(undo)
go func() {
err := a.signedHTTPPost(undo, remoteUser.inbox)
@ -685,10 +707,13 @@ func (a *Actor) Unfollow(user string) {
}
// if there was no error then delete the follow
// from the list
if cancelRequest {
delete(a.requested, user)
} else {
delete(a.following, user)
}
a.save()
}()
}
}
// Announce this activity to our followers

View File

@ -251,6 +251,7 @@ func Serve(actors map[string]Actor) {
// return
// }
actor.following[acceptor] = hash
PrettyPrint(activity)
delete(actor.requested, acceptor)
actor.save()
case "Reject":

View File

@ -2,7 +2,7 @@
## A very light ActivityPub library in go
This library was built to support the very little functions that [pherephone](https://github.com/writeas/pherephone) requires. It might never be feature-complete but it's a very good point to start your activityPub journey. Take a look at [activityserve-example] for a simple main file that uses **activityserve** to post a "Hello, world" message.
This library was built to support the very little functions that [pherephone](https://github.com/writeas/pherephone) requires. It might never be feature-complete but it's a very good point to start your activityPub journey. Take a look at [activityserve-example](https://github.com/writeas/activityserve-example) for a simple main file that uses **activityserve** to post a "Hello, world" message.
For now it supports following and unfollowing users, accepting follows, announcing (boosting) other posts and this is pretty much it.