Jump To …

salmon_interpreter.rb

require_relative './salmon_author'

class SalmonInterpreter
  def initialize(body, params = {})
    raise(ArgumentError, "request body can't be empty") if !body || body.empty?

    @feed = SalmonInterpreter.find_feed(params[:feed_id])

    @salmon = SalmonInterpreter.parse(body)
    raise(ArgumentError, "can't parse salmon envelope") if @salmon.nil?

    @salmon_author = SalmonAuthor.new(@salmon.entry.author)
    @root_url = params[:root_url]
  end

  def interpret

We can ignore salmon for authors that have a local user account.

    return true if local_user?

    @author = find_or_initialize_author
    @author.check_public_key_lease

    raise RstatUs::InvalidSalmonMessage unless message_verified?

When we verify, we know (with some confidence at least) that the salmon notification came from this author. We can then actually commit the author if it is new.

    if @author.new?
      @author.save!
    end

    process_activity
  end

  def process_activity
    case @salmon.entry.activity.verb
    when :post
      post
    when :follow
      follow
    when "http://ostatus.org/schema/1.0/unfollow"
      unfollow
    when "http://ostatus.org/schema/1.0/update-profile"
      update_profile
    end
  end

A new post that perhaps mentions or is in reply to our user

  def post

populate the feed

    @author.feed.populate_entries [@salmon.entry]

Determine reply-to context (if possible)

    thread = @salmon.entry.thr_in_reply_to
    if not thread.nil?
      update_url = thread.href

Local update url

      if update_url.start_with?(@root_url)

Retrieve update id

        update_id = update_url[/#{@root_url}\/updates\/(.*)$/,1]

        u = @author.feed.updates.first :remote_url => @salmon.entry.url
        u.referral_id = update_id
        u.save
      end
    end
  end

A notification that somebody is now following our user

  def follow
    user = @feed.author.user
    if user && !user.followed_by?(@author.feed)
      user.followed_by! @author.feed
    end
  end

  def unfollow
    user = @feed.author.user
    if user && user.followed_by?(@author.feed)
      user.unfollowed_by! @author.feed
    end
  end

  def update_profile

Don’t bother updating the Author if it already has the same info as the SalmonAuthor (for example if we just created it)

    if !(@salmon_author == @author)
      @author.update_attributes!(@salmon_author.author_attributes)
    end
  end

Isolating calls to external classes so we can stub these methods in test and not have to load rails!

  def self.find_feed(id)

Using the bang version so we get a MongoMapper::DocumentNotFound exception if this feed does not exist; the controller catches that exception and renders a 404.

    Feed.find!(id)
  end

  def self.parse(body)
    OStatus::Salmon.from_xml body
  end

  private

  def find_or_initialize_author
    author = Author.first :remote_url => @salmon_author.uri

This author is unknown to us, so let’s create a new Author

    unless author
      author = Author.new(@salmon_author.author_attributes)

Retrieve the user xrd XXX: Use the author uri to determine location of xrd

      remote_host = author.remote_url[/^.*?:\/\/(.*?)\//,1]
      webfinger   = "#{author.username}@#{remote_host}"
      acct        = Redfinger.finger(webfinger)

Retrieve the feed url for the user

      feed_url = acct.links.find { |l| l['rel'] == 'http://schemas.google.com/g/2010#updates-from' }

Retrieve the public key

      public_key = acct.links.find { |l| l['rel'].downcase == 'magic-public-key' }
      public_key = public_key.href[/^.*?,(.*)$/,1]
      author.public_key = public_key
      author.reset_key_lease

Salmon URL

      author.salmon_url = acct.links.find { |l| l['rel'].downcase == 'salmon' }
    end

    author
  end

  def message_verified?
    @salmon.verified?(@author.retrieve_public_key)
  end

  def local_user?
    @salmon_author.uri.start_with?(@root_url)
  end

end