A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

647 行
16 KiB

  1. /*
  2. * Copyright © 2018-2019 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. "bytes"
  13. "crypto/sha256"
  14. "database/sql"
  15. "encoding/base64"
  16. "encoding/json"
  17. "fmt"
  18. "github.com/gorilla/mux"
  19. "github.com/writeas/activity/streams"
  20. "github.com/writeas/httpsig"
  21. "github.com/writeas/impart"
  22. "github.com/writeas/nerds/store"
  23. "github.com/writeas/web-core/activitypub"
  24. "github.com/writeas/web-core/activitystreams"
  25. "github.com/writeas/web-core/log"
  26. "io/ioutil"
  27. "net/http"
  28. "net/http/httputil"
  29. "net/url"
  30. "strconv"
  31. "time"
  32. )
  33. const (
  34. // TODO: delete. don't use this!
  35. apCustomHandleDefault = "blog"
  36. )
  37. type RemoteUser struct {
  38. ID int64
  39. ActorID string
  40. Inbox string
  41. SharedInbox string
  42. }
  43. func (ru *RemoteUser) AsPerson() *activitystreams.Person {
  44. return &activitystreams.Person{
  45. BaseObject: activitystreams.BaseObject{
  46. Type: "Person",
  47. Context: []interface{}{
  48. activitystreams.Namespace,
  49. },
  50. ID: ru.ActorID,
  51. },
  52. Inbox: ru.Inbox,
  53. Endpoints: activitystreams.Endpoints{
  54. SharedInbox: ru.SharedInbox,
  55. },
  56. }
  57. }
  58. func handleFetchCollectionActivities(app *App, w http.ResponseWriter, r *http.Request) error {
  59. w.Header().Set("Server", serverSoftware)
  60. vars := mux.Vars(r)
  61. alias := vars["alias"]
  62. // TODO: enforce visibility
  63. // Get base Collection data
  64. var c *Collection
  65. var err error
  66. if app.cfg.App.SingleUser {
  67. c, err = app.db.GetCollectionByID(1)
  68. } else {
  69. c, err = app.db.GetCollection(alias)
  70. }
  71. if err != nil {
  72. return err
  73. }
  74. p := c.PersonObject()
  75. return impart.RenderActivityJSON(w, p, http.StatusOK)
  76. }
  77. func handleFetchCollectionOutbox(app *App, w http.ResponseWriter, r *http.Request) error {
  78. w.Header().Set("Server", serverSoftware)
  79. vars := mux.Vars(r)
  80. alias := vars["alias"]
  81. // TODO: enforce visibility
  82. // Get base Collection data
  83. var c *Collection
  84. var err error
  85. if app.cfg.App.SingleUser {
  86. c, err = app.db.GetCollectionByID(1)
  87. } else {
  88. c, err = app.db.GetCollection(alias)
  89. }
  90. if err != nil {
  91. return err
  92. }
  93. if app.cfg.App.SingleUser {
  94. if alias != c.Alias {
  95. return ErrCollectionNotFound
  96. }
  97. }
  98. res := &CollectionObj{Collection: *c}
  99. app.db.GetPostsCount(res, false)
  100. accountRoot := c.FederatedAccount()
  101. page := r.FormValue("page")
  102. p, err := strconv.Atoi(page)
  103. if err != nil || p < 1 {
  104. // Return outbox
  105. oc := activitystreams.NewOrderedCollection(accountRoot, "outbox", res.TotalPosts)
  106. return impart.RenderActivityJSON(w, oc, http.StatusOK)
  107. }
  108. // Return outbox page
  109. ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "outbox", res.TotalPosts, p)
  110. ocp.OrderedItems = []interface{}{}
  111. posts, err := app.db.GetPosts(c, p, false, true)
  112. for _, pp := range *posts {
  113. pp.Collection = res
  114. o := pp.ActivityObject()
  115. a := activitystreams.NewCreateActivity(o)
  116. ocp.OrderedItems = append(ocp.OrderedItems, *a)
  117. }
  118. return impart.RenderActivityJSON(w, ocp, http.StatusOK)
  119. }
  120. func handleFetchCollectionFollowers(app *App, w http.ResponseWriter, r *http.Request) error {
  121. w.Header().Set("Server", serverSoftware)
  122. vars := mux.Vars(r)
  123. alias := vars["alias"]
  124. // TODO: enforce visibility
  125. // Get base Collection data
  126. var c *Collection
  127. var err error
  128. if app.cfg.App.SingleUser {
  129. c, err = app.db.GetCollectionByID(1)
  130. } else {
  131. c, err = app.db.GetCollection(alias)
  132. }
  133. if err != nil {
  134. return err
  135. }
  136. accountRoot := c.FederatedAccount()
  137. folls, err := app.db.GetAPFollowers(c)
  138. if err != nil {
  139. return err
  140. }
  141. page := r.FormValue("page")
  142. p, err := strconv.Atoi(page)
  143. if err != nil || p < 1 {
  144. // Return outbox
  145. oc := activitystreams.NewOrderedCollection(accountRoot, "followers", len(*folls))
  146. return impart.RenderActivityJSON(w, oc, http.StatusOK)
  147. }
  148. // Return outbox page
  149. ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "followers", len(*folls), p)
  150. ocp.OrderedItems = []interface{}{}
  151. /*
  152. for _, f := range *folls {
  153. ocp.OrderedItems = append(ocp.OrderedItems, f.ActorID)
  154. }
  155. */
  156. return impart.RenderActivityJSON(w, ocp, http.StatusOK)
  157. }
  158. func handleFetchCollectionFollowing(app *App, w http.ResponseWriter, r *http.Request) error {
  159. w.Header().Set("Server", serverSoftware)
  160. vars := mux.Vars(r)
  161. alias := vars["alias"]
  162. // TODO: enforce visibility
  163. // Get base Collection data
  164. var c *Collection
  165. var err error
  166. if app.cfg.App.SingleUser {
  167. c, err = app.db.GetCollectionByID(1)
  168. } else {
  169. c, err = app.db.GetCollection(alias)
  170. }
  171. if err != nil {
  172. return err
  173. }
  174. accountRoot := c.FederatedAccount()
  175. page := r.FormValue("page")
  176. p, err := strconv.Atoi(page)
  177. if err != nil || p < 1 {
  178. // Return outbox
  179. oc := activitystreams.NewOrderedCollection(accountRoot, "following", 0)
  180. return impart.RenderActivityJSON(w, oc, http.StatusOK)
  181. }
  182. // Return outbox page
  183. ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "following", 0, p)
  184. ocp.OrderedItems = []interface{}{}
  185. return impart.RenderActivityJSON(w, ocp, http.StatusOK)
  186. }
  187. func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request) error {
  188. w.Header().Set("Server", serverSoftware)
  189. vars := mux.Vars(r)
  190. alias := vars["alias"]
  191. var c *Collection
  192. var err error
  193. if app.cfg.App.SingleUser {
  194. c, err = app.db.GetCollectionByID(1)
  195. } else {
  196. c, err = app.db.GetCollection(alias)
  197. }
  198. if err != nil {
  199. // TODO: return Reject?
  200. return err
  201. }
  202. if debugging {
  203. dump, err := httputil.DumpRequest(r, true)
  204. if err != nil {
  205. log.Error("Can't dump: %v", err)
  206. } else {
  207. log.Info("Rec'd! %q", dump)
  208. }
  209. }
  210. var m map[string]interface{}
  211. if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
  212. return err
  213. }
  214. a := streams.NewAccept()
  215. p := c.PersonObject()
  216. var to *url.URL
  217. var isFollow, isUnfollow bool
  218. fullActor := &activitystreams.Person{}
  219. var remoteUser *RemoteUser
  220. res := &streams.Resolver{
  221. FollowCallback: func(f *streams.Follow) error {
  222. isFollow = true
  223. // 1) Use the Follow concrete type here
  224. // 2) Errors are propagated to res.Deserialize call below
  225. m["@context"] = []string{activitystreams.Namespace}
  226. b, _ := json.Marshal(m)
  227. if debugging {
  228. log.Info("Follow: %s", b)
  229. }
  230. _, followID := f.GetId()
  231. if followID == nil {
  232. log.Error("Didn't resolve follow ID")
  233. } else {
  234. aID := c.FederatedAccount() + "#accept-" + store.GenerateFriendlyRandomString(20)
  235. acceptID, err := url.Parse(aID)
  236. if err != nil {
  237. log.Error("Couldn't parse generated Accept URL '%s': %v", aID, err)
  238. }
  239. a.SetId(acceptID)
  240. }
  241. a.AppendObject(f.Raw())
  242. _, to = f.GetActor(0)
  243. obj := f.Raw().GetObjectIRI(0)
  244. a.AppendActor(obj)
  245. // First get actor information
  246. if to == nil {
  247. return fmt.Errorf("No valid `to` string")
  248. }
  249. fullActor, remoteUser, err = getActor(app, to.String())
  250. if err != nil {
  251. return err
  252. }
  253. return impart.RenderActivityJSON(w, m, http.StatusOK)
  254. },
  255. UndoCallback: func(u *streams.Undo) error {
  256. isUnfollow = true
  257. m["@context"] = []string{activitystreams.Namespace}
  258. b, _ := json.Marshal(m)
  259. if debugging {
  260. log.Info("Undo: %s", b)
  261. }
  262. a.AppendObject(u.Raw())
  263. _, to = u.GetActor(0)
  264. // TODO: get actor from object.object, not object
  265. obj := u.Raw().GetObjectIRI(0)
  266. a.AppendActor(obj)
  267. if to != nil {
  268. // Populate fullActor from DB?
  269. remoteUser, err = getRemoteUser(app, to.String())
  270. if err != nil {
  271. if iErr, ok := err.(*impart.HTTPError); ok {
  272. if iErr.Status == http.StatusNotFound {
  273. log.Error("No remoteuser info for Undo event!")
  274. }
  275. }
  276. return err
  277. } else {
  278. fullActor = remoteUser.AsPerson()
  279. }
  280. } else {
  281. log.Error("No to on Undo!")
  282. }
  283. return impart.RenderActivityJSON(w, m, http.StatusOK)
  284. },
  285. }
  286. if err := res.Deserialize(m); err != nil {
  287. // 3) Any errors from #2 can be handled, or the payload is an unknown type.
  288. log.Error("Unable to resolve Follow: %v", err)
  289. if debugging {
  290. log.Error("Map: %s", m)
  291. }
  292. return err
  293. }
  294. go func() {
  295. time.Sleep(2 * time.Second)
  296. am, err := a.Serialize()
  297. if err != nil {
  298. log.Error("Unable to serialize Accept: %v", err)
  299. return
  300. }
  301. am["@context"] = []string{activitystreams.Namespace}
  302. if to == nil {
  303. log.Error("No to! %v", err)
  304. return
  305. }
  306. err = makeActivityPost(p, fullActor.Inbox, am)
  307. if err != nil {
  308. log.Error("Unable to make activity POST: %v", err)
  309. return
  310. }
  311. if isFollow {
  312. t, err := app.db.Begin()
  313. if err != nil {
  314. log.Error("Unable to start transaction: %v", err)
  315. return
  316. }
  317. var followerID int64
  318. if remoteUser != nil {
  319. followerID = remoteUser.ID
  320. } else {
  321. // Add follower locally, since it wasn't found before
  322. res, err := t.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox) VALUES (?, ?, ?)", fullActor.ID, fullActor.Inbox, fullActor.Endpoints.SharedInbox)
  323. if err != nil {
  324. if !app.db.isDuplicateKeyErr(err) {
  325. t.Rollback()
  326. log.Error("Couldn't add new remoteuser in DB: %v\n", err)
  327. return
  328. }
  329. }
  330. followerID, err = res.LastInsertId()
  331. if err != nil {
  332. t.Rollback()
  333. log.Error("no lastinsertid for followers, rolling back: %v", err)
  334. return
  335. }
  336. // Add in key
  337. _, err = t.Exec("INSERT INTO remoteuserkeys (id, remote_user_id, public_key) VALUES (?, ?, ?)", fullActor.PublicKey.ID, followerID, fullActor.PublicKey.PublicKeyPEM)
  338. if err != nil {
  339. if !app.db.isDuplicateKeyErr(err) {
  340. t.Rollback()
  341. log.Error("Couldn't add follower keys in DB: %v\n", err)
  342. return
  343. }
  344. }
  345. }
  346. // Add follow
  347. _, err = t.Exec("INSERT INTO remotefollows (collection_id, remote_user_id, created) VALUES (?, ?, "+app.db.now()+")", c.ID, followerID)
  348. if err != nil {
  349. if !app.db.isDuplicateKeyErr(err) {
  350. t.Rollback()
  351. log.Error("Couldn't add follower in DB: %v\n", err)
  352. return
  353. }
  354. }
  355. err = t.Commit()
  356. if err != nil {
  357. t.Rollback()
  358. log.Error("Rolling back after Commit(): %v\n", err)
  359. return
  360. }
  361. } else if isUnfollow {
  362. // Remove follower locally
  363. _, err = app.db.Exec("DELETE FROM remotefollows WHERE collection_id = ? AND remote_user_id = (SELECT id FROM remoteusers WHERE actor_id = ?)", c.ID, to.String())
  364. if err != nil {
  365. log.Error("Couldn't remove follower from DB: %v\n", err)
  366. }
  367. }
  368. }()
  369. return nil
  370. }
  371. func makeActivityPost(p *activitystreams.Person, url string, m interface{}) error {
  372. log.Info("POST %s", url)
  373. b, err := json.Marshal(m)
  374. if err != nil {
  375. return err
  376. }
  377. r, _ := http.NewRequest("POST", url, bytes.NewBuffer(b))
  378. r.Header.Add("Content-Type", "application/activity+json")
  379. r.Header.Set("User-Agent", "Go ("+serverSoftware+"/"+softwareVer+"; +"+hostName+")")
  380. h := sha256.New()
  381. h.Write(b)
  382. r.Header.Add("Digest", "SHA-256="+base64.StdEncoding.EncodeToString(h.Sum(nil)))
  383. // Sign using the 'Signature' header
  384. privKey, err := activitypub.DecodePrivateKey(p.GetPrivKey())
  385. if err != nil {
  386. return err
  387. }
  388. signer := httpsig.NewSigner(p.PublicKey.ID, privKey, httpsig.RSASHA256, []string{"(request-target)", "date", "host", "digest"})
  389. err = signer.SignSigHeader(r)
  390. if err != nil {
  391. log.Error("Can't sign: %v", err)
  392. }
  393. if debugging {
  394. dump, err := httputil.DumpRequestOut(r, true)
  395. if err != nil {
  396. log.Error("Can't dump: %v", err)
  397. } else {
  398. log.Info("%s", dump)
  399. }
  400. }
  401. resp, err := http.DefaultClient.Do(r)
  402. if err != nil {
  403. return err
  404. }
  405. if resp != nil && resp.Body != nil {
  406. defer resp.Body.Close()
  407. }
  408. body, err := ioutil.ReadAll(resp.Body)
  409. if err != nil {
  410. return err
  411. }
  412. if debugging {
  413. log.Info("Status : %s", resp.Status)
  414. log.Info("Response: %s", body)
  415. }
  416. return nil
  417. }
  418. func resolveIRI(url string) ([]byte, error) {
  419. log.Info("GET %s", url)
  420. r, _ := http.NewRequest("GET", url, nil)
  421. r.Header.Add("Accept", "application/activity+json")
  422. r.Header.Set("User-Agent", "Go ("+serverSoftware+"/"+softwareVer+"; +"+hostName+")")
  423. if debugging {
  424. dump, err := httputil.DumpRequestOut(r, true)
  425. if err != nil {
  426. log.Error("Can't dump: %v", err)
  427. } else {
  428. log.Info("%s", dump)
  429. }
  430. }
  431. resp, err := http.DefaultClient.Do(r)
  432. if err != nil {
  433. return nil, err
  434. }
  435. if resp != nil && resp.Body != nil {
  436. defer resp.Body.Close()
  437. }
  438. body, err := ioutil.ReadAll(resp.Body)
  439. if err != nil {
  440. return nil, err
  441. }
  442. if debugging {
  443. log.Info("Status : %s", resp.Status)
  444. log.Info("Response: %s", body)
  445. }
  446. return body, nil
  447. }
  448. func deleteFederatedPost(app *App, p *PublicPost, collID int64) error {
  449. if debugging {
  450. log.Info("Deleting federated post!")
  451. }
  452. actor := p.Collection.PersonObject(collID)
  453. na := p.ActivityObject()
  454. // Add followers
  455. p.Collection.ID = collID
  456. followers, err := app.db.GetAPFollowers(&p.Collection.Collection)
  457. if err != nil {
  458. log.Error("Couldn't delete post (get followers)! %v", err)
  459. return err
  460. }
  461. inboxes := map[string][]string{}
  462. for _, f := range *followers {
  463. if _, ok := inboxes[f.SharedInbox]; ok {
  464. inboxes[f.SharedInbox] = append(inboxes[f.SharedInbox], f.ActorID)
  465. } else {
  466. inboxes[f.SharedInbox] = []string{f.ActorID}
  467. }
  468. }
  469. for si, instFolls := range inboxes {
  470. na.CC = []string{}
  471. for _, f := range instFolls {
  472. na.CC = append(na.CC, f)
  473. }
  474. err = makeActivityPost(actor, si, activitystreams.NewDeleteActivity(na))
  475. if err != nil {
  476. log.Error("Couldn't delete post! %v", err)
  477. }
  478. }
  479. return nil
  480. }
  481. func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error {
  482. if debugging {
  483. if isUpdate {
  484. log.Info("Federating updated post!")
  485. } else {
  486. log.Info("Federating new post!")
  487. }
  488. }
  489. actor := p.Collection.PersonObject(collID)
  490. na := p.ActivityObject()
  491. // Add followers
  492. p.Collection.ID = collID
  493. followers, err := app.db.GetAPFollowers(&p.Collection.Collection)
  494. if err != nil {
  495. log.Error("Couldn't post! %v", err)
  496. return err
  497. }
  498. log.Info("Followers for %d: %+v", collID, followers)
  499. inboxes := map[string][]string{}
  500. for _, f := range *followers {
  501. if _, ok := inboxes[f.SharedInbox]; ok {
  502. inboxes[f.SharedInbox] = append(inboxes[f.SharedInbox], f.ActorID)
  503. } else {
  504. inboxes[f.SharedInbox] = []string{f.ActorID}
  505. }
  506. }
  507. for si, instFolls := range inboxes {
  508. na.CC = []string{}
  509. for _, f := range instFolls {
  510. na.CC = append(na.CC, f)
  511. }
  512. var activity *activitystreams.Activity
  513. if isUpdate {
  514. activity = activitystreams.NewUpdateActivity(na)
  515. } else {
  516. activity = activitystreams.NewCreateActivity(na)
  517. activity.To = na.To
  518. activity.CC = na.CC
  519. }
  520. err = makeActivityPost(actor, si, activity)
  521. if err != nil {
  522. log.Error("Couldn't post! %v", err)
  523. }
  524. }
  525. return nil
  526. }
  527. func getRemoteUser(app *App, actorID string) (*RemoteUser, error) {
  528. u := RemoteUser{ActorID: actorID}
  529. err := app.db.QueryRow("SELECT id, inbox, shared_inbox FROM remoteusers WHERE actor_id = ?", actorID).Scan(&u.ID, &u.Inbox, &u.SharedInbox)
  530. switch {
  531. case err == sql.ErrNoRows:
  532. return nil, impart.HTTPError{http.StatusNotFound, "No remote user with that ID."}
  533. case err != nil:
  534. log.Error("Couldn't get remote user %s: %v", actorID, err)
  535. return nil, err
  536. }
  537. return &u, nil
  538. }
  539. func getActor(app *App, actorIRI string) (*activitystreams.Person, *RemoteUser, error) {
  540. log.Info("Fetching actor %s locally", actorIRI)
  541. actor := &activitystreams.Person{}
  542. remoteUser, err := getRemoteUser(app, actorIRI)
  543. if err != nil {
  544. if iErr, ok := err.(impart.HTTPError); ok {
  545. if iErr.Status == http.StatusNotFound {
  546. // Fetch remote actor
  547. log.Info("Not found; fetching actor %s remotely", actorIRI)
  548. actorResp, err := resolveIRI(actorIRI)
  549. if err != nil {
  550. log.Error("Unable to get actor! %v", err)
  551. return nil, nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't fetch actor."}
  552. }
  553. if err := json.Unmarshal(actorResp, &actor); err != nil {
  554. // FIXME: Hubzilla has an object for the Actor's url: cannot unmarshal object into Go struct field Person.url of type string
  555. log.Error("Unable to unmarshal actor! %v", err)
  556. return nil, nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't parse actor."}
  557. }
  558. } else {
  559. return nil, nil, err
  560. }
  561. } else {
  562. return nil, nil, err
  563. }
  564. } else {
  565. actor = remoteUser.AsPerson()
  566. }
  567. return actor, remoteUser, nil
  568. }