git-serve: Use `pkgbase` for errors about invalid names
[aur.git] / git-interface / git-serve.py
blobcfd3e3402dd90c00cba28cc431588a3a0cb590b2
1 #!/usr/bin/python3
3 import configparser
4 import mysql.connector
5 import os
6 import pygit2
7 import re
8 import shlex
9 import sys
11 config = configparser.RawConfigParser()
12 config.read(os.path.dirname(os.path.realpath(__file__)) + "/../conf/config")
14 aur_db_host = config.get('database', 'host')
15 aur_db_name = config.get('database', 'name')
16 aur_db_user = config.get('database', 'user')
17 aur_db_pass = config.get('database', 'password')
18 aur_db_socket = config.get('database', 'socket')
20 repo_path = config.get('serve', 'repo-path')
21 repo_regex = config.get('serve', 'repo-regex')
22 git_shell_cmd = config.get('serve', 'git-shell-cmd')
23 ssh_cmdline = config.get('serve', 'ssh-cmdline')
25 def pkgbase_exists(pkgbase):
26 db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
27 passwd=aur_db_pass, db=aur_db_name,
28 unix_socket=aur_db_socket)
29 cur = db.cursor()
31 cur.execute("SELECT COUNT(*) FROM PackageBases WHERE Name = %s ",
32 [pkgbase])
34 db.close()
35 return (cur.fetchone()[0] > 0)
37 def list_repos(user):
38 db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
39 passwd=aur_db_pass, db=aur_db_name,
40 unix_socket=aur_db_socket)
41 cur = db.cursor()
43 cur.execute("SELECT ID FROM Users WHERE Username = %s ", [user])
44 userid = cur.fetchone()[0]
45 if userid == 0:
46 die('%s: unknown user: %s' % (action, user))
48 cur.execute("SELECT Name, PackagerUID FROM PackageBases " +
49 "WHERE MaintainerUID = %s ", [userid])
50 for row in cur:
51 print((' ' if row[1] else '*') + row[0])
52 db.close()
54 def create_pkgbase(pkgbase, user):
55 if not re.match(repo_regex, pkgbase):
56 die('%s: invalid repository name: %s' % (action, pkgbase))
57 if pkgbase_exists(pkgbase):
58 die('%s: package base already exists: %s' % (action, pkgbase))
60 db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
61 passwd=aur_db_pass, db=aur_db_name,
62 unix_socket=aur_db_socket)
63 cur = db.cursor()
65 cur.execute("SELECT ID FROM Users WHERE Username = %s ", [user])
66 userid = cur.fetchone()[0]
67 if userid == 0:
68 die('%s: unknown user: %s' % (action, user))
70 cur.execute("INSERT INTO PackageBases (Name, SubmittedTS, ModifiedTS, " +
71 "SubmitterUID, MaintainerUID) VALUES (%s, UNIX_TIMESTAMP(), " +
72 "UNIX_TIMESTAMP(), %s, %s)", [pkgbase, userid, userid])
73 pkgbase_id = cur.lastrowid
75 cur.execute("INSERT INTO CommentNotify (PackageBaseID, UserID) " +
76 "VALUES (%s, %s)", [pkgbase_id, userid])
78 db.commit()
79 db.close()
81 def setup_repo(pkgbase):
82 if not re.match(repo_regex, pkgbase):
83 die('%s: invalid repository name: %s' % (action, pkgbase))
85 repo = pygit2.Repository(repo_path)
86 refs = repo.listall_references()
88 if not 'refs/heads/' + pkgbase in refs:
89 repo.create_reference('refs/heads/' + pkgbase, 'refs/namespaces/' +
90 pkgbase + '/refs/heads/master')
91 if not 'refs/namespaces/' + pkgbase + '/HEAD' in refs:
92 repo.create_reference('refs/namespaces/' + pkgbase + '/HEAD',
93 'refs/namespaces/' + pkgbase +
94 '/refs/heads/master')
96 def check_permissions(pkgbase, user):
97 db = mysql.connector.connect(host=aur_db_host, user=aur_db_user,
98 passwd=aur_db_pass, db=aur_db_name,
99 unix_socket=aur_db_socket, buffered=True)
100 cur = db.cursor()
102 cur.execute("SELECT AccountTypeID FROM Users WHERE UserName = %s ", [user])
103 if cur.fetchone()[0] > 1:
104 return True
106 cur.execute("SELECT COUNT(*) FROM PackageBases " +
107 "LEFT JOIN PackageComaintainers " +
108 "ON PackageComaintainers.PackageBaseID = PackageBases.ID " +
109 "INNER JOIN Users ON Users.ID = PackageBases.MaintainerUID " +
110 "OR PackageBases.MaintainerUID IS NULL " +
111 "OR Users.ID = PackageComaintainers.UsersID " +
112 "WHERE Name = %s AND Username = %s", [pkgbase, user])
113 return cur.fetchone()[0] > 0
115 def die(msg):
116 sys.stderr.write("%s\n" % (msg))
117 exit(1)
119 def die_with_help(msg):
120 die(msg + "\nTry `%s help` for a list of commands." % (ssh_cmdline))
122 user = sys.argv[1]
123 cmd = os.environ.get("SSH_ORIGINAL_COMMAND")
124 if not cmd:
125 die_with_help("Interactive shell is disabled.")
126 cmdargv = shlex.split(cmd)
127 action = cmdargv[0]
129 if action == 'git-upload-pack' or action == 'git-receive-pack':
130 if len(cmdargv) < 2:
131 die_with_help("%s: missing path" % (action))
133 path = cmdargv[1].rstrip('/')
134 if not path.startswith('/') or not path.endswith('.git'):
135 die('%s: invalid path: %s' % (action, path))
136 pkgbase = path[1:-4]
137 if not re.match(repo_regex, pkgbase):
138 die('%s: invalid repository name: %s' % (action, pkgbase))
140 if not pkgbase_exists(pkgbase):
141 create_pkgbase(pkgbase, user)
142 setup_repo(pkgbase);
144 if action == 'git-receive-pack':
145 if not check_permissions(pkgbase, user):
146 die('%s: permission denied: %s' % (action, user))
148 os.environ["AUR_USER"] = user
149 os.environ["AUR_PKGBASE"] = pkgbase
150 os.environ["GIT_NAMESPACE"] = pkgbase
151 cmd = action + " '" + repo_path + "'"
152 os.execl(git_shell_cmd, git_shell_cmd, '-c', cmd)
153 elif action == 'list-repos':
154 if len(cmdargv) > 1:
155 die_with_help("%s: too many arguments" % (action))
156 list_repos(user)
157 elif action == 'setup-repo':
158 if len(cmdargv) < 2:
159 die_with_help("%s: missing repository name" % (action))
160 if len(cmdargv) > 2:
161 die_with_help("%s: too many arguments" % (action))
162 create_pkgbase(cmdargv[1], user)
163 elif action == 'help':
164 die("Commands:\n" +
165 " help Show this help message and exit.\n" +
166 " list-repos List all your repositories.\n" +
167 " setup-repo <name> Create an empty repository.\n" +
168 " git-receive-pack Internal command used with Git.\n" +
169 " git-upload-pack Internal command used with Git.")
170 else:
171 die_with_help("invalid command: %s" % (action))