A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 
 

940 righe
25 KiB

  1. /*
  2. * Copyright © 2018-2021 Musing Studio 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. "io"
  19. "net/http"
  20. "net/http/httputil"
  21. "net/url"
  22. "path/filepath"
  23. "strconv"
  24. "strings"
  25. "time"
  26. "github.com/gorilla/mux"
  27. "github.com/writeas/activity/streams"
  28. "github.com/writeas/activityserve"
  29. "github.com/writeas/httpsig"
  30. "github.com/writeas/impart"
  31. "github.com/writeas/web-core/activitypub"
  32. "github.com/writeas/web-core/activitystreams"
  33. "github.com/writeas/web-core/id"
  34. "github.com/writeas/web-core/log"
  35. "github.com/writeas/web-core/silobridge"
  36. )
  37. const (
  38. // TODO: delete. don't use this!
  39. apCustomHandleDefault = "blog"
  40. apCacheTime = time.Minute
  41. )
  42. var instanceColl *Collection
  43. func initActivityPub(app *App) {
  44. ur, _ := url.Parse(app.cfg.App.Host)
  45. instanceColl = &Collection{
  46. ID: 0,
  47. Alias: ur.Host,
  48. Title: ur.Host,
  49. db: app.db,
  50. hostName: app.cfg.App.Host,
  51. }
  52. }
  53. type RemoteUser struct {
  54. ID int64
  55. ActorID string
  56. Inbox string
  57. SharedInbox string
  58. URL string
  59. Handle string
  60. }
  61. func (ru *RemoteUser) AsPerson() *activitystreams.Person {
  62. return &activitystreams.Person{
  63. BaseObject: activitystreams.BaseObject{
  64. Type: "Person",
  65. Context: []interface{}{
  66. activitystreams.Namespace,
  67. },
  68. ID: ru.ActorID,
  69. },
  70. Inbox: ru.Inbox,
  71. Endpoints: activitystreams.Endpoints{
  72. SharedInbox: ru.SharedInbox,
  73. },
  74. }
  75. }
  76. func activityPubClient() *http.Client {
  77. return &http.Client{
  78. Timeout: 15 * time.Second,
  79. }
  80. }
  81. func handleFetchCollectionActivities(app *App, w http.ResponseWriter, r *http.Request) error {
  82. w.Header().Set("Server", serverSoftware)
  83. vars := mux.Vars(r)
  84. alias := vars["alias"]
  85. if alias == "" {
  86. alias = filepath.Base(r.RequestURI)
  87. }
  88. // TODO: enforce visibility
  89. // Get base Collection data
  90. var c *Collection
  91. var err error
  92. if alias == r.Host {
  93. c = instanceColl
  94. } else if app.cfg.App.SingleUser {
  95. c, err = app.db.GetCollectionByID(1)
  96. } else {
  97. c, err = app.db.GetCollection(alias)
  98. }
  99. if err != nil {
  100. return err
  101. }
  102. c.hostName = app.cfg.App.Host
  103. if !c.IsInstanceColl() {
  104. silenced, err := app.db.IsUserSilenced(c.OwnerID)
  105. if err != nil {
  106. log.Error("fetch collection activities: %v", err)
  107. return ErrInternalGeneral
  108. }
  109. if silenced {
  110. return ErrCollectionNotFound
  111. }
  112. }
  113. p := c.PersonObject()
  114. setCacheControl(w, apCacheTime)
  115. return impart.RenderActivityJSON(w, p, http.StatusOK)
  116. }
  117. func handleFetchCollectionOutbox(app *App, w http.ResponseWriter, r *http.Request) error {
  118. w.Header().Set("Server", serverSoftware)
  119. vars := mux.Vars(r)
  120. alias := vars["alias"]
  121. // TODO: enforce visibility
  122. // Get base Collection data
  123. var c *Collection
  124. var err error
  125. if app.cfg.App.SingleUser {
  126. c, err = app.db.GetCollectionByID(1)
  127. } else {
  128. c, err = app.db.GetCollection(alias)
  129. }
  130. if err != nil {
  131. return err
  132. }
  133. silenced, err := app.db.IsUserSilenced(c.OwnerID)
  134. if err != nil {
  135. log.Error("fetch collection outbox: %v", err)
  136. return ErrInternalGeneral
  137. }
  138. if silenced {
  139. return ErrCollectionNotFound
  140. }
  141. c.hostName = app.cfg.App.Host
  142. if app.cfg.App.SingleUser {
  143. if alias != c.Alias {
  144. return ErrCollectionNotFound
  145. }
  146. }
  147. res := &CollectionObj{Collection: *c}
  148. app.db.GetPostsCount(res, false)
  149. accountRoot := c.FederatedAccount()
  150. page := r.FormValue("page")
  151. p, err := strconv.Atoi(page)
  152. if err != nil || p < 1 {
  153. // Return outbox
  154. oc := activitystreams.NewOrderedCollection(accountRoot, "outbox", res.TotalPosts)
  155. return impart.RenderActivityJSON(w, oc, http.StatusOK)
  156. }
  157. // Return outbox page
  158. ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "outbox", res.TotalPosts, p)
  159. ocp.OrderedItems = []interface{}{}
  160. posts, err := app.db.GetPosts(app.cfg, c, p, false, true, false)
  161. for _, pp := range *posts {
  162. pp.Collection = res
  163. o := pp.ActivityObject(app)
  164. a := activitystreams.NewCreateActivity(o)
  165. a.Context = nil
  166. ocp.OrderedItems = append(ocp.OrderedItems, *a)
  167. }
  168. setCacheControl(w, apCacheTime)
  169. return impart.RenderActivityJSON(w, ocp, http.StatusOK)
  170. }
  171. func handleFetchCollectionFollowers(app *App, w http.ResponseWriter, r *http.Request) error {
  172. w.Header().Set("Server", serverSoftware)
  173. vars := mux.Vars(r)
  174. alias := vars["alias"]
  175. // TODO: enforce visibility
  176. // Get base Collection data
  177. var c *Collection
  178. var err error
  179. if app.cfg.App.SingleUser {
  180. c, err = app.db.GetCollectionByID(1)
  181. } else {
  182. c, err = app.db.GetCollection(alias)
  183. }
  184. if err != nil {
  185. return err
  186. }
  187. silenced, err := app.db.IsUserSilenced(c.OwnerID)
  188. if err != nil {
  189. log.Error("fetch collection followers: %v", err)
  190. return ErrInternalGeneral
  191. }
  192. if silenced {
  193. return ErrCollectionNotFound
  194. }
  195. c.hostName = app.cfg.App.Host
  196. accountRoot := c.FederatedAccount()
  197. folls, err := app.db.GetAPFollowers(c)
  198. if err != nil {
  199. return err
  200. }
  201. page := r.FormValue("page")
  202. p, err := strconv.Atoi(page)
  203. if err != nil || p < 1 {
  204. // Return outbox
  205. oc := activitystreams.NewOrderedCollection(accountRoot, "followers", len(*folls))
  206. return impart.RenderActivityJSON(w, oc, http.StatusOK)
  207. }
  208. // Return outbox page
  209. ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "followers", len(*folls), p)
  210. ocp.OrderedItems = []interface{}{}
  211. /*
  212. for _, f := range *folls {
  213. ocp.OrderedItems = append(ocp.OrderedItems, f.ActorID)
  214. }
  215. */
  216. setCacheControl(w, apCacheTime)
  217. return impart.RenderActivityJSON(w, ocp, http.StatusOK)
  218. }
  219. func handleFetchCollectionFollowing(app *App, w http.ResponseWriter, r *http.Request) error {
  220. w.Header().Set("Server", serverSoftware)
  221. vars := mux.Vars(r)
  222. alias := vars["alias"]
  223. // TODO: enforce visibility
  224. // Get base Collection data
  225. var c *Collection
  226. var err error
  227. if app.cfg.App.SingleUser {
  228. c, err = app.db.GetCollectionByID(1)
  229. } else {
  230. c, err = app.db.GetCollection(alias)
  231. }
  232. if err != nil {
  233. return err
  234. }
  235. silenced, err := app.db.IsUserSilenced(c.OwnerID)
  236. if err != nil {
  237. log.Error("fetch collection following: %v", err)
  238. return ErrInternalGeneral
  239. }
  240. if silenced {
  241. return ErrCollectionNotFound
  242. }
  243. c.hostName = app.cfg.App.Host
  244. accountRoot := c.FederatedAccount()
  245. page := r.FormValue("page")
  246. p, err := strconv.Atoi(page)
  247. if err != nil || p < 1 {
  248. // Return outbox
  249. oc := activitystreams.NewOrderedCollection(accountRoot, "following", 0)
  250. return impart.RenderActivityJSON(w, oc, http.StatusOK)
  251. }
  252. // Return outbox page
  253. ocp := activitystreams.NewOrderedCollectionPage(accountRoot, "following", 0, p)
  254. ocp.OrderedItems = []interface{}{}
  255. setCacheControl(w, apCacheTime)
  256. return impart.RenderActivityJSON(w, ocp, http.StatusOK)
  257. }
  258. func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request) error {
  259. w.Header().Set("Server", serverSoftware)
  260. vars := mux.Vars(r)
  261. alias := vars["alias"]
  262. var c *Collection
  263. var err error
  264. if app.cfg.App.SingleUser {
  265. c, err = app.db.GetCollectionByID(1)
  266. } else {
  267. c, err = app.db.GetCollection(alias)
  268. }
  269. if err != nil {
  270. // TODO: return Reject?
  271. return err
  272. }
  273. silenced, err := app.db.IsUserSilenced(c.OwnerID)
  274. if err != nil {
  275. log.Error("fetch collection inbox: %v", err)
  276. return ErrInternalGeneral
  277. }
  278. if silenced {
  279. return ErrCollectionNotFound
  280. }
  281. c.hostName = app.cfg.App.Host
  282. if debugging {
  283. dump, err := httputil.DumpRequest(r, true)
  284. if err != nil {
  285. log.Error("Can't dump: %v", err)
  286. } else {
  287. log.Info("Rec'd! %q", dump)
  288. }
  289. }
  290. var m map[string]interface{}
  291. if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
  292. return err
  293. }
  294. a := streams.NewAccept()
  295. p := c.PersonObject()
  296. var to *url.URL
  297. var isFollow, isUnfollow bool
  298. fullActor := &activitystreams.Person{}
  299. var remoteUser *RemoteUser
  300. res := &streams.Resolver{
  301. FollowCallback: func(f *streams.Follow) error {
  302. isFollow = true
  303. // 1) Use the Follow concrete type here
  304. // 2) Errors are propagated to res.Deserialize call below
  305. m["@context"] = []string{activitystreams.Namespace}
  306. b, _ := json.Marshal(m)
  307. if debugging {
  308. log.Info("Follow: %s", b)
  309. }
  310. _, followID := f.GetId()
  311. if followID == nil {
  312. log.Error("Didn't resolve follow ID")
  313. } else {
  314. aID := c.FederatedAccount() + "#accept-" + id.GenerateFriendlyRandomString(20)
  315. acceptID, err := url.Parse(aID)
  316. if err != nil {
  317. log.Error("Couldn't parse generated Accept URL '%s': %v", aID, err)
  318. }
  319. a.SetId(acceptID)
  320. }
  321. a.AppendObject(f.Raw())
  322. _, to = f.GetActor(0)
  323. obj := f.Raw().GetObjectIRI(0)
  324. a.AppendActor(obj)
  325. // First get actor information
  326. if to == nil {
  327. return fmt.Errorf("No valid `to` string")
  328. }
  329. fullActor, remoteUser, err = getActor(app, to.String())
  330. if err != nil {
  331. return err
  332. }
  333. return impart.RenderActivityJSON(w, m, http.StatusOK)
  334. },
  335. UndoCallback: func(u *streams.Undo) error {
  336. isUnfollow = true
  337. m["@context"] = []string{activitystreams.Namespace}
  338. b, _ := json.Marshal(m)
  339. if debugging {
  340. log.Info("Undo: %s", b)
  341. }
  342. a.AppendObject(u.Raw())
  343. _, to = u.GetActor(0)
  344. // TODO: get actor from object.object, not object
  345. obj := u.Raw().GetObjectIRI(0)
  346. a.AppendActor(obj)
  347. if to != nil {
  348. // Populate fullActor from DB?
  349. remoteUser, err = getRemoteUser(app, to.String())
  350. if err != nil {
  351. if iErr, ok := err.(*impart.HTTPError); ok {
  352. if iErr.Status == http.StatusNotFound {
  353. log.Error("No remoteuser info for Undo event!")
  354. }
  355. }
  356. return err
  357. } else {
  358. fullActor = remoteUser.AsPerson()
  359. }
  360. } else {
  361. log.Error("No to on Undo!")
  362. }
  363. return impart.RenderActivityJSON(w, m, http.StatusOK)
  364. },
  365. }
  366. if err := res.Deserialize(m); err != nil {
  367. // 3) Any errors from #2 can be handled, or the payload is an unknown type.
  368. log.Error("Unable to resolve Follow: %v", err)
  369. if debugging {
  370. log.Error("Map: %s", m)
  371. }
  372. return err
  373. }
  374. go func() {
  375. if to == nil {
  376. if debugging {
  377. log.Error("No `to` value!")
  378. }
  379. return
  380. }
  381. time.Sleep(2 * time.Second)
  382. am, err := a.Serialize()
  383. if err != nil {
  384. log.Error("Unable to serialize Accept: %v", err)
  385. return
  386. }
  387. am["@context"] = []string{activitystreams.Namespace}
  388. err = makeActivityPost(app.cfg.App.Host, p, fullActor.Inbox, am)
  389. if err != nil {
  390. log.Error("Unable to make activity POST: %v", err)
  391. return
  392. }
  393. if isFollow {
  394. t, err := app.db.Begin()
  395. if err != nil {
  396. log.Error("Unable to start transaction: %v", err)
  397. return
  398. }
  399. var followerID int64
  400. if remoteUser != nil {
  401. followerID = remoteUser.ID
  402. } else {
  403. // Add follower locally, since it wasn't found before
  404. res, err := t.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox, url) VALUES (?, ?, ?, ?)", fullActor.ID, fullActor.Inbox, fullActor.Endpoints.SharedInbox, fullActor.URL)
  405. if err != nil {
  406. // if duplicate key, res will be nil and panic on
  407. // res.LastInsertId below
  408. t.Rollback()
  409. log.Error("Couldn't add new remoteuser in DB: %v\n", err)
  410. return
  411. }
  412. followerID, err = res.LastInsertId()
  413. if err != nil {
  414. t.Rollback()
  415. log.Error("no lastinsertid for followers, rolling back: %v", err)
  416. return
  417. }
  418. // Add in key
  419. _, err = t.Exec("INSERT INTO remoteuserkeys (id, remote_user_id, public_key) VALUES (?, ?, ?)", fullActor.PublicKey.ID, followerID, fullActor.PublicKey.PublicKeyPEM)
  420. if err != nil {
  421. if !app.db.isDuplicateKeyErr(err) {
  422. t.Rollback()
  423. log.Error("Couldn't add follower keys in DB: %v\n", err)
  424. return
  425. }
  426. }
  427. }
  428. // Add follow
  429. _, err = t.Exec("INSERT INTO remotefollows (collection_id, remote_user_id, created) VALUES (?, ?, "+app.db.now()+")", c.ID, followerID)
  430. if err != nil {
  431. if !app.db.isDuplicateKeyErr(err) {
  432. t.Rollback()
  433. log.Error("Couldn't add follower in DB: %v\n", err)
  434. return
  435. }
  436. }
  437. err = t.Commit()
  438. if err != nil {
  439. t.Rollback()
  440. log.Error("Rolling back after Commit(): %v\n", err)
  441. return
  442. }
  443. } else if isUnfollow {
  444. // Remove follower locally
  445. _, 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())
  446. if err != nil {
  447. log.Error("Couldn't remove follower from DB: %v\n", err)
  448. }
  449. }
  450. }()
  451. return nil
  452. }
  453. func makeActivityPost(hostName string, p *activitystreams.Person, url string, m interface{}) error {
  454. log.Info("POST %s", url)
  455. b, err := json.Marshal(m)
  456. if err != nil {
  457. return err
  458. }
  459. r, _ := http.NewRequest("POST", url, bytes.NewBuffer(b))
  460. r.Header.Add("Content-Type", "application/activity+json")
  461. r.Header.Set("User-Agent", ServerUserAgent(hostName))
  462. h := sha256.New()
  463. h.Write(b)
  464. r.Header.Add("Digest", "SHA-256="+base64.StdEncoding.EncodeToString(h.Sum(nil)))
  465. // Sign using the 'Signature' header
  466. privKey, err := activitypub.DecodePrivateKey(p.GetPrivKey())
  467. if err != nil {
  468. return err
  469. }
  470. signer := httpsig.NewSigner(p.PublicKey.ID, privKey, httpsig.RSASHA256, []string{"(request-target)", "date", "host", "digest"})
  471. err = signer.SignSigHeader(r)
  472. if err != nil {
  473. log.Error("Can't sign: %v", err)
  474. }
  475. if debugging {
  476. dump, err := httputil.DumpRequestOut(r, true)
  477. if err != nil {
  478. log.Error("Can't dump: %v", err)
  479. } else {
  480. log.Info("%s", dump)
  481. }
  482. }
  483. resp, err := activityPubClient().Do(r)
  484. if err != nil {
  485. return err
  486. }
  487. if resp != nil && resp.Body != nil {
  488. defer resp.Body.Close()
  489. }
  490. body, err := io.ReadAll(resp.Body)
  491. if err != nil {
  492. return err
  493. }
  494. if debugging {
  495. log.Info("Status : %s", resp.Status)
  496. log.Info("Response: %s", body)
  497. }
  498. return nil
  499. }
  500. func resolveIRI(hostName, url string) ([]byte, error) {
  501. log.Info("GET %s", url)
  502. r, _ := http.NewRequest("GET", url, nil)
  503. r.Header.Add("Accept", "application/activity+json")
  504. r.Header.Set("User-Agent", ServerUserAgent(hostName))
  505. p := instanceColl.PersonObject()
  506. h := sha256.New()
  507. h.Write([]byte{})
  508. r.Header.Add("Digest", "SHA-256="+base64.StdEncoding.EncodeToString(h.Sum(nil)))
  509. // Sign using the 'Signature' header
  510. privKey, err := activitypub.DecodePrivateKey(p.GetPrivKey())
  511. if err != nil {
  512. return nil, err
  513. }
  514. signer := httpsig.NewSigner(p.PublicKey.ID, privKey, httpsig.RSASHA256, []string{"(request-target)", "date", "host", "digest"})
  515. err = signer.SignSigHeader(r)
  516. if err != nil {
  517. log.Error("Can't sign: %v", err)
  518. }
  519. if debugging {
  520. dump, err := httputil.DumpRequestOut(r, true)
  521. if err != nil {
  522. log.Error("Can't dump: %v", err)
  523. } else {
  524. log.Info("%s", dump)
  525. }
  526. }
  527. resp, err := activityPubClient().Do(r)
  528. if err != nil {
  529. return nil, err
  530. }
  531. if resp != nil && resp.Body != nil {
  532. defer resp.Body.Close()
  533. }
  534. body, err := io.ReadAll(resp.Body)
  535. if err != nil {
  536. return nil, err
  537. }
  538. if debugging {
  539. log.Info("Status : %s", resp.Status)
  540. log.Info("Response: %s", body)
  541. }
  542. return body, nil
  543. }
  544. func deleteFederatedPost(app *App, p *PublicPost, collID int64) error {
  545. if debugging {
  546. log.Info("Deleting federated post!")
  547. }
  548. p.Collection.hostName = app.cfg.App.Host
  549. actor := p.Collection.PersonObject(collID)
  550. na := p.ActivityObject(app)
  551. // Add followers
  552. p.Collection.ID = collID
  553. followers, err := app.db.GetAPFollowers(&p.Collection.Collection)
  554. if err != nil {
  555. log.Error("Couldn't delete post (get followers)! %v", err)
  556. return err
  557. }
  558. inboxes := map[string][]string{}
  559. for _, f := range *followers {
  560. inbox := f.SharedInbox
  561. if inbox == "" {
  562. inbox = f.Inbox
  563. }
  564. if _, ok := inboxes[inbox]; ok {
  565. inboxes[inbox] = append(inboxes[inbox], f.ActorID)
  566. } else {
  567. inboxes[inbox] = []string{f.ActorID}
  568. }
  569. }
  570. for si, instFolls := range inboxes {
  571. na.CC = []string{}
  572. na.CC = append(na.CC, instFolls...)
  573. da := activitystreams.NewDeleteActivity(na)
  574. // Make the ID unique to ensure it works in Pleroma
  575. // See: https://git.pleroma.social/pleroma/pleroma/issues/1481
  576. da.ID += "#Delete"
  577. err = makeActivityPost(app.cfg.App.Host, actor, si, da)
  578. if err != nil {
  579. log.Error("Couldn't delete post! %v", err)
  580. }
  581. }
  582. return nil
  583. }
  584. func federatePost(app *App, p *PublicPost, collID int64, isUpdate bool) error {
  585. // If app is private, do not federate
  586. if app.cfg.App.Private {
  587. return nil
  588. }
  589. // Do not federate posts from private or protected blogs
  590. if p.Collection.Visibility == CollPrivate || p.Collection.Visibility == CollProtected {
  591. return nil
  592. }
  593. if debugging {
  594. if isUpdate {
  595. log.Info("Federating updated post!")
  596. } else {
  597. log.Info("Federating new post!")
  598. }
  599. }
  600. actor := p.Collection.PersonObject(collID)
  601. na := p.ActivityObject(app)
  602. // Add followers
  603. p.Collection.ID = collID
  604. followers, err := app.db.GetAPFollowers(&p.Collection.Collection)
  605. if err != nil {
  606. log.Error("Couldn't post! %v", err)
  607. return err
  608. }
  609. log.Info("Followers for %d: %+v", collID, followers)
  610. inboxes := map[string][]string{}
  611. for _, f := range *followers {
  612. inbox := f.SharedInbox
  613. if inbox == "" {
  614. inbox = f.Inbox
  615. }
  616. if _, ok := inboxes[inbox]; ok {
  617. // check if we're already sending to this shared inbox
  618. inboxes[inbox] = append(inboxes[inbox], f.ActorID)
  619. } else {
  620. // add the new shared inbox to the list
  621. inboxes[inbox] = []string{f.ActorID}
  622. }
  623. }
  624. var activity *activitystreams.Activity
  625. // for each one of the shared inboxes
  626. for si, instFolls := range inboxes {
  627. // add all followers from that instance
  628. // to the CC field
  629. na.CC = []string{}
  630. na.CC = append(na.CC, instFolls...)
  631. // create a new "Create" activity
  632. // with our article as object
  633. if isUpdate {
  634. activity = activitystreams.NewUpdateActivity(na)
  635. } else {
  636. activity = activitystreams.NewCreateActivity(na)
  637. activity.To = na.To
  638. activity.CC = na.CC
  639. }
  640. // and post it to that sharedInbox
  641. err = makeActivityPost(app.cfg.App.Host, actor, si, activity)
  642. if err != nil {
  643. log.Error("Couldn't post! %v", err)
  644. }
  645. }
  646. // re-create the object so that the CC list gets reset and has
  647. // the mentioned users. This might seem wasteful but the code is
  648. // cleaner than adding the mentioned users to CC here instead of
  649. // in p.ActivityObject()
  650. na = p.ActivityObject(app)
  651. for _, tag := range na.Tag {
  652. if tag.Type == "Mention" {
  653. activity = activitystreams.NewCreateActivity(na)
  654. activity.To = na.To
  655. activity.CC = na.CC
  656. // This here might be redundant in some cases as we might have already
  657. // sent this to the sharedInbox of this instance above, but we need too
  658. // much logic to catch this at the expense of the odd extra request.
  659. // I don't believe we'd ever have too many mentions in a single post that this
  660. // could become a burden.
  661. remoteUser, err := getRemoteUser(app, tag.HRef)
  662. if err != nil {
  663. log.Error("Unable to find remote user %s. Skipping: %v", tag.HRef, err)
  664. continue
  665. }
  666. err = makeActivityPost(app.cfg.App.Host, actor, remoteUser.Inbox, activity)
  667. if err != nil {
  668. log.Error("Couldn't post! %v", err)
  669. }
  670. }
  671. }
  672. return nil
  673. }
  674. func getRemoteUser(app *App, actorID string) (*RemoteUser, error) {
  675. u := RemoteUser{ActorID: actorID}
  676. var urlVal, handle sql.NullString
  677. err := app.db.QueryRow("SELECT id, inbox, shared_inbox, url, handle FROM remoteusers WHERE actor_id = ?", actorID).Scan(&u.ID, &u.Inbox, &u.SharedInbox, &urlVal, &handle)
  678. switch {
  679. case err == sql.ErrNoRows:
  680. return nil, impart.HTTPError{http.StatusNotFound, "No remote user with that ID."}
  681. case err != nil:
  682. log.Error("Couldn't get remote user %s: %v", actorID, err)
  683. return nil, err
  684. }
  685. u.URL = urlVal.String
  686. u.Handle = handle.String
  687. return &u, nil
  688. }
  689. // getRemoteUserFromHandle retrieves the profile page of a remote user
  690. // from the @user@server.tld handle
  691. func getRemoteUserFromHandle(app *App, handle string) (*RemoteUser, error) {
  692. u := RemoteUser{Handle: handle}
  693. var urlVal sql.NullString
  694. err := app.db.QueryRow("SELECT id, actor_id, inbox, shared_inbox, url FROM remoteusers WHERE handle = ?", handle).Scan(&u.ID, &u.ActorID, &u.Inbox, &u.SharedInbox, &urlVal)
  695. switch {
  696. case err == sql.ErrNoRows:
  697. return nil, ErrRemoteUserNotFound
  698. case err != nil:
  699. log.Error("Couldn't get remote user %s: %v", handle, err)
  700. return nil, err
  701. }
  702. u.URL = urlVal.String
  703. return &u, nil
  704. }
  705. func getActor(app *App, actorIRI string) (*activitystreams.Person, *RemoteUser, error) {
  706. log.Info("Fetching actor %s locally", actorIRI)
  707. actor := &activitystreams.Person{}
  708. remoteUser, err := getRemoteUser(app, actorIRI)
  709. if err != nil {
  710. if iErr, ok := err.(impart.HTTPError); ok {
  711. if iErr.Status == http.StatusNotFound {
  712. // Fetch remote actor
  713. log.Info("Not found; fetching actor %s remotely", actorIRI)
  714. actorResp, err := resolveIRI(app.cfg.App.Host, actorIRI)
  715. if err != nil {
  716. log.Error("Unable to get actor! %v", err)
  717. return nil, nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't fetch actor."}
  718. }
  719. if err := unmarshalActor(actorResp, actor); err != nil {
  720. log.Error("Unable to unmarshal actor! %v", err)
  721. return nil, nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't parse actor."}
  722. }
  723. } else {
  724. return nil, nil, err
  725. }
  726. } else {
  727. return nil, nil, err
  728. }
  729. } else {
  730. actor = remoteUser.AsPerson()
  731. }
  732. return actor, remoteUser, nil
  733. }
  734. func GetProfileURLFromHandle(app *App, handle string) (string, error) {
  735. handle = strings.TrimLeft(handle, "@")
  736. actorIRI := ""
  737. parts := strings.Split(handle, "@")
  738. if len(parts) != 2 {
  739. return "", fmt.Errorf("invalid handle format")
  740. }
  741. domain := parts[1]
  742. // Check non-AP instances
  743. if siloProfileURL := silobridge.Profile(parts[0], domain); siloProfileURL != "" {
  744. return siloProfileURL, nil
  745. }
  746. remoteUser, err := getRemoteUserFromHandle(app, handle)
  747. if err != nil {
  748. // can't find using handle in the table but the table may already have this user without
  749. // handle from a previous version
  750. // TODO: Make this determination. We should know whether a user exists without a handle, or doesn't exist at all
  751. actorIRI = RemoteLookup(handle)
  752. _, errRemoteUser := getRemoteUser(app, actorIRI)
  753. // if it exists then we need to update the handle
  754. if errRemoteUser == nil {
  755. _, err := app.db.Exec("UPDATE remoteusers SET handle = ? WHERE actor_id = ?", handle, actorIRI)
  756. if err != nil {
  757. log.Error("Couldn't update handle '%s' for user %s", handle, actorIRI)
  758. }
  759. } else {
  760. // this probably means we don't have the user in the table so let's try to insert it
  761. // here we need to ask the server for the inboxes
  762. remoteActor, err := activityserve.NewRemoteActor(actorIRI)
  763. if err != nil {
  764. log.Error("Couldn't fetch remote actor: %v", err)
  765. }
  766. if debugging {
  767. log.Info("Got remote actor: %s %s %s %s %s", actorIRI, remoteActor.GetInbox(), remoteActor.GetSharedInbox(), remoteActor.URL(), handle)
  768. }
  769. _, err = app.db.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox, url, handle) VALUES(?, ?, ?, ?, ?)", actorIRI, remoteActor.GetInbox(), remoteActor.GetSharedInbox(), remoteActor.URL(), handle)
  770. if err != nil {
  771. log.Error("Couldn't insert remote user: %v", err)
  772. return "", err
  773. }
  774. actorIRI = remoteActor.URL()
  775. }
  776. } else if remoteUser.URL == "" {
  777. log.Info("Remote user %s URL empty, fetching", remoteUser.ActorID)
  778. newRemoteActor, err := activityserve.NewRemoteActor(remoteUser.ActorID)
  779. if err != nil {
  780. log.Error("Couldn't fetch remote actor: %v", err)
  781. } else {
  782. _, err := app.db.Exec("UPDATE remoteusers SET url = ? WHERE actor_id = ?", newRemoteActor.URL(), remoteUser.ActorID)
  783. if err != nil {
  784. log.Error("Couldn't update handle '%s' for user %s", handle, actorIRI)
  785. } else {
  786. actorIRI = newRemoteActor.URL()
  787. }
  788. }
  789. } else {
  790. actorIRI = remoteUser.URL
  791. }
  792. return actorIRI, nil
  793. }
  794. // unmarshal actor normalizes the actor response to conform to
  795. // the type Person from github.com/writeas/web-core/activitysteams
  796. //
  797. // some implementations return different context field types
  798. // this converts any non-slice contexts into a slice
  799. func unmarshalActor(actorResp []byte, actor *activitystreams.Person) error {
  800. // FIXME: Hubzilla has an object for the Actor's url: cannot unmarshal object into Go struct field Person.url of type string
  801. // flexActor overrides the Context field to allow
  802. // all valid representations during unmarshal
  803. flexActor := struct {
  804. activitystreams.Person
  805. Context json.RawMessage `json:"@context,omitempty"`
  806. }{}
  807. if err := json.Unmarshal(actorResp, &flexActor); err != nil {
  808. return err
  809. }
  810. actor.Endpoints = flexActor.Endpoints
  811. actor.Followers = flexActor.Followers
  812. actor.Following = flexActor.Following
  813. actor.ID = flexActor.ID
  814. actor.Icon = flexActor.Icon
  815. actor.Inbox = flexActor.Inbox
  816. actor.Name = flexActor.Name
  817. actor.Outbox = flexActor.Outbox
  818. actor.PreferredUsername = flexActor.PreferredUsername
  819. actor.PublicKey = flexActor.PublicKey
  820. actor.Summary = flexActor.Summary
  821. actor.Type = flexActor.Type
  822. actor.URL = flexActor.URL
  823. func(val interface{}) {
  824. switch val.(type) {
  825. case []interface{}:
  826. // already a slice, do nothing
  827. actor.Context = val.([]interface{})
  828. default:
  829. actor.Context = []interface{}{val}
  830. }
  831. }(flexActor.Context)
  832. return nil
  833. }
  834. func setCacheControl(w http.ResponseWriter, ttl time.Duration) {
  835. w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%.0f", ttl.Seconds()))
  836. }