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