Cleanup
							parent
							
								
									76f9e14b93
								
							
						
					
					
						commit
						5fc3b48e70
					
				
							
								
								
									
										19
									
								
								TODO
								
								
								
								
							
							
						
						
									
										19
									
								
								TODO
								
								
								
								
							| 
						 | 
					@ -13,7 +13,7 @@
 | 
				
			||||||
        [✔] Handle being followed
 | 
					        [✔] Handle being followed
 | 
				
			||||||
        [✔] When followed, the handler should write the new follower to file
 | 
					        [✔] When followed, the handler should write the new follower to file
 | 
				
			||||||
        [✔] Make sure we send our boosts to all our followers
 | 
					        [✔] 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 
 | 
					[✔] Write all the announcements (boosts) to the database to 
 | 
				
			||||||
    their correct actors
 | 
					    their correct actors
 | 
				
			||||||
[✔] Check if we are already following users
 | 
					[✔] Check if we are already following users
 | 
				
			||||||
| 
						 | 
					@ -22,12 +22,13 @@
 | 
				
			||||||
[✔] Make OS-independent (mosty directory separators)
 | 
					[✔] Make OS-independent (mosty directory separators)
 | 
				
			||||||
[✔] Create outbox.json programmatically
 | 
					[✔] Create outbox.json programmatically
 | 
				
			||||||
[✔] Make storage configurable (search for "storage" in project)
 | 
					[✔] 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
 | 
					    through in our inbox
 | 
				
			||||||
[✔] Boost not only articles but other things too
 | 
					[✔] Boost not only articles but other things too
 | 
				
			||||||
[✔] Sanitize input, never allow slashes or dots
 | 
					[✔] Sanitize input, never allow slashes or dots
 | 
				
			||||||
[✔] Add summary to actors.json
 | 
					[✔] 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
 | 
					[✔] Create debug flag
 | 
				
			||||||
[✔] Write to following only upon accept
 | 
					[✔] Write to following only upon accept
 | 
				
			||||||
    (waiting to actually get an accept so that I can test this)
 | 
					    (waiting to actually get an accept so that I can test this)
 | 
				
			||||||
| 
						 | 
					@ -41,16 +42,16 @@
 | 
				
			||||||
[ ] Implement nodeinfo and statistics
 | 
					[ ] Implement nodeinfo and statistics
 | 
				
			||||||
[✔] Accept even if already follows us
 | 
					[✔] Accept even if already follows us
 | 
				
			||||||
[✔] Handle paging
 | 
					[✔] Handle paging
 | 
				
			||||||
[ ] Test paging
 | 
					[✔] Test paging
 | 
				
			||||||
[✔] Handle http signatures
 | 
					[✔] Handle http signatures
 | 
				
			||||||
[ ] Verify http signatures
 | 
					[ ] Verify http signatures
 | 
				
			||||||
[ ] Refactor, comment and clean up
 | 
					[✔] Refactor, comment and clean up
 | 
				
			||||||
[ ] Split to pherephone and activityServe
 | 
					[✔] Split to pherephone and activityServe
 | 
				
			||||||
[ ] Decide what's to be done with actors removed from `actors.json`.
 | 
					[ ] Decide what's to be done with actors removed from `actors.json`.
 | 
				
			||||||
    [ ] Remove them?
 | 
					    [ ] Remove them?
 | 
				
			||||||
    [ ] Leave them read-only?
 | 
					    [ ] Leave them read-only?
 | 
				
			||||||
    [ ] Leave them as is?
 | 
					    [✔] Leave them as is?
 | 
				
			||||||
[✔] Handle followers and following uri's
 | 
					[✔] Handle followers and following uri's
 | 
				
			||||||
[ ] Do I care about the inbox?
 | 
					[ ] Do I care about the inbox?
 | 
				
			||||||
[ ] Expose configuration to apps
 | 
					[✔] Expose configuration to apps
 | 
				
			||||||
[ ] Do not boost replies (configurable)
 | 
					[✔] Do not boost replies (configurable)
 | 
				
			||||||
							
								
								
									
										79
									
								
								actor.go
								
								
								
								
							
							
						
						
									
										79
									
								
								actor.go
								
								
								
								
							| 
						 | 
					@ -54,8 +54,8 @@ type ActorToSave struct {
 | 
				
			||||||
	Followers, Following, Rejected, Requested            map[string]interface{}
 | 
						Followers, Following, Rejected, Requested            map[string]interface{}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MakeActor returns a new local actor we can act
 | 
					// MakeActor creates and returns a new local actor we can act
 | 
				
			||||||
// on behalf of
 | 
					// on behalf of. It also creates its files on disk
 | 
				
			||||||
func MakeActor(name, summary, actorType string) (Actor, error) {
 | 
					func MakeActor(name, summary, actorType string) (Actor, error) {
 | 
				
			||||||
	followers := make(map[string]interface{})
 | 
						followers := make(map[string]interface{})
 | 
				
			||||||
	following := make(map[string]interface{})
 | 
						following := make(map[string]interface{})
 | 
				
			||||||
| 
						 | 
					@ -125,7 +125,7 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
 | 
				
			||||||
	return actor, nil
 | 
						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 {
 | 
					func (a *Actor) GetOutboxIRI() *url.URL {
 | 
				
			||||||
	iri := a.iri + "/outbox"
 | 
						iri := a.iri + "/outbox"
 | 
				
			||||||
	nuiri, _ := url.Parse(iri)
 | 
						nuiri, _ := url.Parse(iri)
 | 
				
			||||||
| 
						 | 
					@ -133,7 +133,8 @@ func (a *Actor) GetOutboxIRI() *url.URL {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadActor searches the filesystem and creates an Actor
 | 
					// 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) {
 | 
					func LoadActor(name string) (Actor, error) {
 | 
				
			||||||
	// make sure our users can't read our hard drive
 | 
						// make sure our users can't read our hard drive
 | 
				
			||||||
	if strings.ContainsAny(name, "./ ") {
 | 
						if strings.ContainsAny(name, "./ ") {
 | 
				
			||||||
| 
						 | 
					@ -161,9 +162,6 @@ func LoadActor(name string) (Actor, error) {
 | 
				
			||||||
		return Actor{}, err
 | 
							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)))
 | 
						publicKeyDecoded, rest := pem.Decode([]byte(jsonData["PublicKey"].(string)))
 | 
				
			||||||
	if publicKeyDecoded == nil {
 | 
						if publicKeyDecoded == nil {
 | 
				
			||||||
		log.Info(rest)
 | 
							log.Info(rest)
 | 
				
			||||||
| 
						 | 
					@ -245,7 +243,7 @@ func GetActor(name, summary, actorType string) (Actor, error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// func LoadActorFromIRI(iri string) a Actor{
 | 
					// func LoadActorFromIRI(iri string) a Actor{
 | 
				
			||||||
 | 
					// TODO, this should parse the iri and load the right actor
 | 
				
			||||||
// }
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// save the actor to file
 | 
					// save the actor to file
 | 
				
			||||||
| 
						 | 
					@ -445,6 +443,9 @@ func (a *Actor) GetFollowing(page int) (response []byte, err error) {
 | 
				
			||||||
	return a.getPeers(page, "following")
 | 
						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) {
 | 
					func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err error) {
 | 
				
			||||||
	b, err := json.Marshal(content)
 | 
						b, err := json.Marshal(content)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -498,7 +499,7 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	responseData, _ := ioutil.ReadAll(resp.Body)
 | 
						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
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -556,6 +557,8 @@ func (a *Actor) NewFollower(iri string, inbox string) error {
 | 
				
			||||||
	return a.save()
 | 
						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) {
 | 
					func (a *Actor) appendToOutbox(iri string) (err error) {
 | 
				
			||||||
	// create outbox file if it doesn't exist
 | 
						// create outbox file if it doesn't exist
 | 
				
			||||||
	var outbox *os.File
 | 
						var outbox *os.File
 | 
				
			||||||
| 
						 | 
					@ -574,6 +577,7 @@ func (a *Actor) appendToOutbox(iri string) (err error) {
 | 
				
			||||||
	return nil
 | 
						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) {
 | 
					func (a *Actor) batchSend(activity map[string]interface{}, recipients []string) (err error) {
 | 
				
			||||||
	for _, v := range recipients {
 | 
						for _, v := range recipients {
 | 
				
			||||||
		err := a.signedHTTPPost(activity, v)
 | 
							err := a.signedHTTPPost(activity, v)
 | 
				
			||||||
| 
						 | 
					@ -584,6 +588,7 @@ func (a *Actor) batchSend(activity map[string]interface{}, recipients []string)
 | 
				
			||||||
	return
 | 
						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) {
 | 
					func (a *Actor) sendToFollowers(activity map[string]interface{}) (err error) {
 | 
				
			||||||
	recipients := make([]string, len(a.followers))
 | 
						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
 | 
					// was accepted when initially following that user
 | 
				
			||||||
// (this is read from the `actor.following` map
 | 
					// (this is read from the `actor.following` map
 | 
				
			||||||
func (a *Actor) Unfollow(user string) {
 | 
					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)
 | 
						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
 | 
						// create an undo activiy
 | 
				
			||||||
	undo := make(map[string]interface{})
 | 
						undo := make(map[string]interface{})
 | 
				
			||||||
	undo["@context"] = context()
 | 
						undo["@context"] = context()
 | 
				
			||||||
	undo["actor"] = a.iri
 | 
						undo["actor"] = a.iri
 | 
				
			||||||
 | 
						undo["id"] = baseURL + "/item/" + hash + "/undo"
 | 
				
			||||||
	// find the id of the original follow
 | 
						undo["type"] = "Undo"
 | 
				
			||||||
	hash := a.following[user].(string)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	follow := make(map[string]interface{})
 | 
						follow := make(map[string]interface{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -673,22 +697,23 @@ func (a *Actor) Unfollow(user string) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// only if we're already following them
 | 
						PrettyPrint(undo)
 | 
				
			||||||
	if _, ok := a.following[user]; ok {
 | 
						go func() {
 | 
				
			||||||
		PrettyPrint(undo)
 | 
							err := a.signedHTTPPost(undo, remoteUser.inbox)
 | 
				
			||||||
		go func() {
 | 
							if err != nil {
 | 
				
			||||||
			err := a.signedHTTPPost(undo, remoteUser.inbox)
 | 
								log.Info("Couldn't unfollow " + user)
 | 
				
			||||||
			if err != nil {
 | 
								log.Info(err)
 | 
				
			||||||
				log.Info("Couldn't unfollow " + user)
 | 
								return
 | 
				
			||||||
				log.Info(err)
 | 
							}
 | 
				
			||||||
				return
 | 
							// if there was no error then delete the follow
 | 
				
			||||||
			}
 | 
							// from the list
 | 
				
			||||||
			// if there was no error then delete the follow
 | 
							if cancelRequest {
 | 
				
			||||||
			// from the list
 | 
								delete(a.requested, user)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
			delete(a.following, user)
 | 
								delete(a.following, user)
 | 
				
			||||||
			a.save()
 | 
							}
 | 
				
			||||||
		}()
 | 
							a.save()
 | 
				
			||||||
	}
 | 
						}()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Announce this activity to our followers
 | 
					// Announce this activity to our followers
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								http.go
								
								
								
								
							
							
						
						
									
										1
									
								
								http.go
								
								
								
								
							| 
						 | 
					@ -251,6 +251,7 @@ func Serve(actors map[string]Actor) {
 | 
				
			||||||
			// 	return
 | 
								// 	return
 | 
				
			||||||
			// }
 | 
								// }
 | 
				
			||||||
			actor.following[acceptor] = hash
 | 
								actor.following[acceptor] = hash
 | 
				
			||||||
 | 
								PrettyPrint(activity)
 | 
				
			||||||
			delete(actor.requested, acceptor)
 | 
								delete(actor.requested, acceptor)
 | 
				
			||||||
			actor.save()
 | 
								actor.save()
 | 
				
			||||||
		case "Reject":
 | 
							case "Reject":
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## A very light ActivityPub library in go
 | 
					## 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. 
 | 
					For now it supports following and unfollowing users, accepting follows, announcing (boosting) other posts and this is pretty much it. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue