A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

141 lines
3.4 KiB

  1. /*
  2. * Copyright © 2018-2020 A Bunch Tell LLC.
  3. *
  4. * This file is part of WriteFreely.
  5. *
  6. * WriteFreely is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License, included
  8. * in the LICENSE file in this source code package.
  9. */
  10. package writefreely
  11. import (
  12. "encoding/json"
  13. "io/ioutil"
  14. "net/http"
  15. "strings"
  16. "github.com/writeas/go-webfinger"
  17. "github.com/writeas/impart"
  18. "github.com/writeas/web-core/log"
  19. "github.com/writeas/writefreely/config"
  20. )
  21. type wfResolver struct {
  22. db *datastore
  23. cfg *config.Config
  24. }
  25. var wfUserNotFoundErr = impart.HTTPError{http.StatusNotFound, "User not found."}
  26. func (wfr wfResolver) FindUser(username string, host, requestHost string, r []webfinger.Rel) (*webfinger.Resource, error) {
  27. var c *Collection
  28. var err error
  29. if wfr.cfg.App.SingleUser {
  30. c, err = wfr.db.GetCollectionByID(1)
  31. } else {
  32. c, err = wfr.db.GetCollection(username)
  33. }
  34. if err != nil {
  35. log.Error("Unable to get blog: %v", err)
  36. return nil, err
  37. }
  38. silenced, err := wfr.db.IsUserSilenced(c.OwnerID)
  39. if err != nil {
  40. log.Error("webfinger find user: check is silenced: %v", err)
  41. return nil, err
  42. }
  43. if silenced {
  44. return nil, wfUserNotFoundErr
  45. }
  46. c.hostName = wfr.cfg.App.Host
  47. if wfr.cfg.App.SingleUser {
  48. // Ensure handle matches user-chosen one on single-user blogs
  49. if username != c.Alias {
  50. log.Info("Username '%s' is not handle '%s'", username, c.Alias)
  51. return nil, wfUserNotFoundErr
  52. }
  53. }
  54. // Only return information if site has federation enabled.
  55. // TODO: enable two levels of federation? Unlisted or Public on timelines?
  56. if !wfr.cfg.App.Federation {
  57. return nil, wfUserNotFoundErr
  58. }
  59. res := webfinger.Resource{
  60. Subject: "acct:" + username + "@" + host,
  61. Aliases: []string{
  62. c.CanonicalURL(),
  63. c.FederatedAccount(),
  64. },
  65. Links: []webfinger.Link{
  66. {
  67. HRef: c.CanonicalURL(),
  68. Type: "text/html",
  69. Rel: "https://webfinger.net/rel/profile-page",
  70. },
  71. {
  72. HRef: c.FederatedAccount(),
  73. Type: "application/activity+json",
  74. Rel: "self",
  75. },
  76. },
  77. }
  78. return &res, nil
  79. }
  80. func (wfr wfResolver) DummyUser(username string, hostname string, r []webfinger.Rel) (*webfinger.Resource, error) {
  81. return nil, wfUserNotFoundErr
  82. }
  83. func (wfr wfResolver) IsNotFoundError(err error) bool {
  84. return err == wfUserNotFoundErr
  85. }
  86. // RemoteLookup looks up a user by handle at a remote server
  87. // and returns the actor URL
  88. func RemoteLookup(handle string) string {
  89. handle = strings.TrimLeft(handle, "@")
  90. // let's take the server part of the handle
  91. parts := strings.Split(handle, "@")
  92. resp, err := http.Get("https://" + parts[1] + "/.well-known/webfinger?resource=acct:" + handle)
  93. if err != nil {
  94. log.Error("Error performing webfinger request", err)
  95. return ""
  96. }
  97. body, err := ioutil.ReadAll(resp.Body)
  98. if err != nil {
  99. log.Error("Error reading webfinger response", err)
  100. return ""
  101. }
  102. var result webfinger.Resource
  103. err = json.Unmarshal(body, &result)
  104. if err != nil {
  105. log.Error("Unsupported webfinger response received: %v", err)
  106. return ""
  107. }
  108. var href string
  109. // iterate over webfinger links and find the one with
  110. // a self "rel"
  111. for _, link := range result.Links {
  112. if link.Rel == "self" {
  113. href = link.HRef
  114. }
  115. }
  116. // if we didn't find it with the above then
  117. // try using aliases
  118. if href == "" {
  119. // take the last alias because mastodon has the
  120. // https://instance.tld/@user first which
  121. // doesn't work as an href
  122. href = result.Aliases[len(result.Aliases)-1]
  123. }
  124. return href
  125. }