Skip to content

Commit

Permalink
Decompiler Middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
nviennot committed Apr 20, 2013
1 parent 4901824 commit 44e6778
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 31 deletions.
3 changes: 2 additions & 1 deletion app/models/repository.rb
Expand Up @@ -2,7 +2,7 @@ class Repository < Rugged::Repository
REPO_PATH = Rails.root.join 'repos'
class EmptyCommit < RuntimeError; end

attr_accessor :app_id
attr_accessor :app_id, :path

# Rugged gets its hands on new, not initialize
def self.new(app_id, options={})
Expand All @@ -14,6 +14,7 @@ def self.new(app_id, options={})

super(path.to_s).instance_eval do
@app_id = app_id
@path = path
self
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/stack.rb
Expand Up @@ -7,7 +7,7 @@ def self.process_free_app(app_id)
use PrepareFS
use FetchMarketDetails
use DownloadApk
# use DecompileApk
use DecompileApk
end
@create_app_stack.call :app_id => app_id, :crawl_date => Date.today
end
Expand Down
60 changes: 39 additions & 21 deletions lib/stack/base_git.rb
@@ -1,5 +1,6 @@
class Stack::BaseGit < Stack::Base
class Git
class CommitError < RuntimeError; end
include ActionView::Helpers::TextHelper
include ActionView::Helpers::SanitizeHelper

Expand All @@ -12,7 +13,6 @@ def initialize(env, options={})
@branch = options[:branch]
@role = options[:role]
@tag_with = options[:tag_with]
@tree_builder = TreeBuilderProxy.new(repo)
end

def app
Expand Down Expand Up @@ -43,6 +43,10 @@ def last_commit_sha
Rugged::Reference.lookup(repo, branch_ref).try(:target)
end

def last_committed_tree
repo.lookup(last_commit_sha).tree
end

def new_branch?
!last_commit_sha
end
Expand All @@ -59,37 +63,36 @@ def full_message
msg += "\n\nVersion Code: #{app.version_code}"
end

class TreeBuilderProxy < Rugged::Tree::Builder
def initialize(repo)
@repo = repo
super()
class Index < Rugged::Index
def self.new(repo)
super().instance_eval do
@repo = repo
self
end
end

def add_file(name, content, mode=0100644)
def add_file(filename, content, mode=0100644)
oid = @repo.write(content, :blob)
self.insert(:type => :blob, :name => name.to_s, :oid => oid, :filemode => mode)
@has_file = true
self.add(:path => filename.to_s, :oid => oid, :mode => mode)
end

def has_files?
@has_file
def add_dir(dir)
prefix_regexp = Regexp.new("^#{dir}/")
Dir["#{dir}/**/*"].each do |filename|
next unless File.file?(filename)
add_file(filename.gsub(prefix_regexp, ''), File.open(filename, 'rb') { |f| f.read })
end
end

def write
def write_tree
super(@repo)
end
end

def read_file(file_name)
file = repo.lookup(last_commit_sha).tree[file_name]
return nil unless file
repo.lookup(file[:oid]).read_raw.data
end

def commit(&block)
block.call(tree_builder)

raise "No files to commit" unless tree_builder.has_files?
index = Index.new(repo)
block.call(index)
raise CommitError.new "No files to commit" unless index.count > 0

commit = {}

Expand All @@ -107,12 +110,27 @@ def commit(&block)

commit[:parents] = [last_commit_sha].compact
commit[:update_ref] = branch_ref
commit[:tree] = tree_builder.write
commit[:tree] = index.write_tree

Rugged::Commit.create(repo, commit).tap do |commit_sha|
Rugged::Reference.create(repo, tag_ref, commit_sha)
end
end

def read_file(file_name)
file = last_committed_tree[file_name]
return nil unless file
repo.lookup(file[:oid]).read_raw.data
end

def read_files(&block)
last_committed_tree.walk(:preorder) do |dir, entry|
case entry[:type]
when :tree then block.call("#{dir}#{entry[:name]}", nil)
when :blob then block.call("#{dir}#{entry[:name]}", repo.lookup(entry[:oid]).read_raw.data)
end
end
end
end

class << self
Expand Down
33 changes: 30 additions & 3 deletions lib/stack/decompile_apk.rb
@@ -1,5 +1,32 @@
class Stack::DecompileApk < Stack::Base
def call(env)
@stack.call(env)
require 'fileutils'

class Stack::DecompileApk < Stack::BaseGit
class DecompilationError < RuntimeError; end

use_git :branch => :src

def persist_to_git(env, git)
output = `script/decompile #{env[:scratch]} #{env[:apk_path].basename}`
raise DecompilationError.new(output) unless $?.success?

env[:src_dir] = env[:scratch].join('src')

git.commit do |index|
index.add_dir(env[:src_dir])
end
end

def parse_from_git(env, git)
env[:src_dir] = env[:scratch].join('src')
FileUtils.mkpath(env[:src_dir])

git.read_files do |filename, content|
path = env[:src_dir].join(filename)
if content
File.open(path, 'wb') { |f| f.write(content) }
else
FileUtils.mkpath(path)
end
end
end
end
4 changes: 2 additions & 2 deletions lib/stack/download_apk.rb
Expand Up @@ -32,8 +32,8 @@ def persist_to_git(env, git)

apk_filename = "#{env[:app].id_version}.apk"

git.commit do |tree|
tree.add_file(apk_filename, response.body)
git.commit do |index|
index.add_file(apk_filename, response.body)
end

env[:apk_path] = env[:scratch].join(apk_filename)
Expand Down
4 changes: 2 additions & 2 deletions lib/stack/fetch_market_details.rb
Expand Up @@ -9,8 +9,8 @@ def persist_to_git(env, git)

env[:app] = App.from_market(app_details.raw_app)

git.commit do |tree|
tree.add_file('metadata.json', MultiJson.dump(app_details.raw_app, :pretty => true))
git.commit do |index|
index.add_file('metadata.json', MultiJson.dump(app_details.raw_app, :pretty => true))
end
end

Expand Down
5 changes: 5 additions & 0 deletions lib/stack/prepare_fs.rb
Expand Up @@ -9,6 +9,11 @@ def call(env)
Dir.mktmpdir "#{env[:app_id]}", Rails.root.join('scratch') do |dir|
env[:scratch] = Pathname.new(dir)
@stack.call(env)

# It would be much more efficient to write a pack directly.
# Expect horrible performance when saving sources.
output = `cd #{env[:repo].path} 2>&1 && git gc --prune=now -q 2>&1`
Rails.logger.info "Cannot garbage collect the repository: #{output}" unless $?.success?
end
end
end
3 changes: 2 additions & 1 deletion script/decompile
Expand Up @@ -5,4 +5,5 @@
APK2GOLD=`pwd`/vendor/apk2gold/apk2gold
ulimit -m $((1024*1024))
cd $1
$APK2GOLD $2 2>&1
$APK2GOLD $2 2>&1 || exit 1
mv "${2%.*}" src

0 comments on commit 44e6778

Please sign in to comment.