A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

656 linhas
16 KiB

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