Merge branch 'feature/rails3' into next
[gitorious:mainline.git] / lib / gitorious / ssh / client.rb
1 # encoding: utf-8
2 #--
3 #   Copyright (C) 2012 Gitorious AS
4 #   Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
5 #   Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
6 #   Copyright (C) 2007, 2008 Johan Sørensen <johan@johansorensen.com>
7 #
8 #   This program is free software: you can redistribute it and/or modify
9 #   it under the terms of the GNU Affero General Public License as published by
10 #   the Free Software Foundation, either version 3 of the License, or
11 #   (at your option) any later version.
12 #
13 #   This program is distributed in the hope that it will be useful,
14 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #   GNU Affero General Public License for more details.
17 #
18 #   You should have received a copy of the GNU Affero General Public License
19 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #++
21
22 require "net/http"
23 require "uri"
24 require "gitorious/configuration_loader"
25 require File.expand_path(File.join(File.dirname(__FILE__), "../../../app/models/repository_root.rb"))
26
27 module Gitorious
28   module SSH
29     class AccessDeniedError < StandardError; end
30
31     class Client
32       def initialize(strainer, username)
33         @strainer = strainer
34         @project_name, @repository_name = strainer.path.split("/", 2)
35         @repository_name.gsub!(/\.git$/, "")
36         @user_name = username
37         @configuration = {}
38         load_config
39       end
40       attr_accessor :project_name, :repository_name, :user_name
41
42       def writable_by_user?
43         query_for_permission_and_path
44         @writable == "true"
45       end
46
47       def assure_user_can_write!
48         writable_by_user? || raise(AccessDeniedError)
49       end
50
51       # The full URL
52       def writable_by_query_url
53         writable_by_query_uri.to_s
54       end
55
56       # The path only
57       def writable_by_query_path
58         writable_by_query_uri.request_uri
59       end
60
61       def real_path
62         if !configuration["real_path"] || configuration["real_path"] == "nil"
63           raise AccessDeniedError
64         end
65         full_real_path = File.join(RepositoryRoot.default_base_path,
66                                    configuration["real_path"])
67         raise AccessDeniedError unless File.exist?(full_real_path)
68         full_real_path
69       end
70
71       def force_pushing_denied?
72         configuration["force_pushing_denied"] == "true"
73       end
74
75       def to_git_shell_argument
76         "#{@strainer.verb} '#{real_path}'"
77       end
78
79       def pre_receive_hook_exists?
80         filename = File.join(real_path, "hooks", "pre-receive")
81         File.executable?(filename)
82       end
83
84       def configuration
85         if @configuration.empty?
86           query_url = "/#{@project_name}/#{@repository_name}/config?username=#@user_name"
87           # $stderr.puts "Querying #{query_url}" if $DEBUG
88           resp = connection.get(query_url)
89           # $stderr.puts resp
90           parse_configuration(resp.body)
91         end
92         @configuration
93       end
94
95       protected
96       def parse_configuration(raw_config)
97         raw_config.split("\n").each do |line|
98           key, val = line.split(":")
99           if key && val
100             @configuration[key] = val
101           end
102         end
103       end
104
105       def connection
106         host = Gitorious.client.host
107         port = Gitorious.client.port
108         @connection ||= Net::HTTP.start(host, port)
109       end
110
111       def load_config
112         return if Gitorious.respond_to?(:client)
113         Gitorious::ConfigurationLoader.new.configure_singletons(RAILS_ENV)
114       end
115
116       # Returns an actual URI object
117       def writable_by_query_uri
118         path = "/#{@project_name}/#{@repository_name}/writable_by"
119         query = "username=#{@user_name}"
120         host = Gitorious.client.host
121         _port = Gitorious.client.port
122         # Ruby 1.9 expects a number, while 1.8 expects a string. Oh well
123         port = RUBY_VERSION > "1.9" ? _port : _port.to_s
124
125         URI::HTTP.build(:host => host, :port => port, :path => path, :query => query)
126       end
127     end
128   end
129 end