A clean, Markdown-based publishing platform made for writers. Write together, and build a community. https://writefreely.org
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 

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