git-interface: Do not use UNIX_TIMESTAMP
[aur.git] / git-interface / git-serve.py
blobd3a32c39db4bf569ed09d8ba940fd45ea5222f23
1 #!/usr/bin/python3
3 import os
4 import re
5 import shlex
6 import sys
7 import time
9 import config
10 import db
12 repo_path = config.get('serve', 'repo-path')
13 repo_regex = config.get('serve', 'repo-regex')
14 git_shell_cmd = config.get('serve', 'git-shell-cmd')
15 git_update_cmd = config.get('serve', 'git-update-cmd')
16 ssh_cmdline = config.get('serve', 'ssh-cmdline')
18 enable_maintenance = config.getboolean('options', 'enable-maintenance')
19 maintenance_exc = config.get('options', 'maintenance-exceptions').split()
22 def pkgbase_from_name(pkgbase):
23 conn = db.Connection()
24 cur = conn.execute("SELECT ID FROM PackageBases WHERE Name = ?", [pkgbase])
26 row = cur.fetchone()
27 return row[0] if row else None
30 def pkgbase_exists(pkgbase):
31 return pkgbase_from_name(pkgbase) is not None
34 def list_repos(user):
35 conn = db.Connection()
37 cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user])
38 userid = cur.fetchone()[0]
39 if userid == 0:
40 die('{:s}: unknown user: {:s}'.format(action, user))
42 cur = conn.execute("SELECT Name, PackagerUID FROM PackageBases " +
43 "WHERE MaintainerUID = ?", [userid])
44 for row in cur:
45 print((' ' if row[1] else '*') + row[0])
46 conn.close()
49 def create_pkgbase(pkgbase, user):
50 if not re.match(repo_regex, pkgbase):
51 die('{:s}: invalid repository name: {:s}'.format(action, pkgbase))
52 if pkgbase_exists(pkgbase):
53 die('{:s}: package base already exists: {:s}'.format(action, pkgbase))
55 conn = db.Connection()
57 cur = conn.execute("SELECT ID FROM Users WHERE Username = ?", [user])
58 userid = cur.fetchone()[0]
59 if userid == 0:
60 die('{:s}: unknown user: {:s}'.format(action, user))
62 now = int(time.time())
63 cur = conn.execute("INSERT INTO PackageBases (Name, SubmittedTS, " +
64 "ModifiedTS, SubmitterUID, MaintainerUID) VALUES " +
65 "(?, ?, ?, ?, ?)", [pkgbase, now, now, userid, userid])
66 pkgbase_id = cur.lastrowid
68 cur = conn.execute("INSERT INTO PackageNotifications " +
69 "(PackageBaseID, UserID) VALUES (?, ?)",
70 [pkgbase_id, userid])
72 conn.commit()
73 conn.close()
76 def pkgbase_set_keywords(pkgbase, keywords):
77 pkgbase_id = pkgbase_from_name(pkgbase)
78 if not pkgbase_id:
79 die('{:s}: package base not found: {:s}'.format(action, pkgbase))
81 conn = db.Connection()
83 conn.execute("DELETE FROM PackageKeywords WHERE PackageBaseID = ?",
84 [pkgbase_id])
85 for keyword in keywords:
86 conn.execute("INSERT INTO PackageKeywords (PackageBaseID, Keyword) " +
87 "VALUES (?, ?)", [pkgbase_id, keyword])
89 conn.commit()
90 conn.close()
93 def pkgbase_has_write_access(pkgbase, user):
94 conn = db.Connection()
96 cur = conn.execute("SELECT COUNT(*) FROM PackageBases " +
97 "LEFT JOIN PackageComaintainers " +
98 "ON PackageComaintainers.PackageBaseID = PackageBases.ID " +
99 "INNER JOIN Users " +
100 "ON Users.ID = PackageBases.MaintainerUID " +
101 "OR PackageBases.MaintainerUID IS NULL " +
102 "OR Users.ID = PackageComaintainers.UsersID " +
103 "WHERE Name = ? AND Username = ?", [pkgbase, user])
104 return cur.fetchone()[0] > 0
107 def die(msg):
108 sys.stderr.write("{:s}\n".format(msg))
109 exit(1)
112 def die_with_help(msg):
113 die(msg + "\nTry `{:s} help` for a list of commands.".format(ssh_cmdline))
116 def warn(msg):
117 sys.stderr.write("warning: {:s}\n".format(msg))
120 user = os.environ.get('AUR_USER')
121 privileged = (os.environ.get('AUR_PRIVILEGED', '0') == '1')
122 ssh_cmd = os.environ.get('SSH_ORIGINAL_COMMAND')
123 ssh_client = os.environ.get('SSH_CLIENT')
125 if not ssh_cmd:
126 die_with_help("Interactive shell is disabled.")
127 cmdargv = shlex.split(ssh_cmd)
128 action = cmdargv[0]
129 remote_addr = ssh_client.split(' ')[0] if ssh_client else None
131 if enable_maintenance:
132 if remote_addr not in maintenance_exc:
133 die("The AUR is down due to maintenance. We will be back soon.")
135 if action == 'git-upload-pack' or action == 'git-receive-pack':
136 if len(cmdargv) < 2:
137 die_with_help("{:s}: missing path".format(action))
139 path = cmdargv[1].rstrip('/')
140 if not path.startswith('/'):
141 path = '/' + path
142 if not path.endswith('.git'):
143 path = path + '.git'
144 pkgbase = path[1:-4]
145 if not re.match(repo_regex, pkgbase):
146 die('{:s}: invalid repository name: {:s}'.format(action, pkgbase))
148 if not pkgbase_exists(pkgbase):
149 create_pkgbase(pkgbase, user)
151 if action == 'git-receive-pack':
152 if not privileged and not pkgbase_has_write_access(pkgbase, user):
153 die('{:s}: permission denied: {:s}'.format(action, user))
155 os.environ["AUR_USER"] = user
156 os.environ["AUR_PKGBASE"] = pkgbase
157 os.environ["GIT_NAMESPACE"] = pkgbase
158 cmd = action + " '" + repo_path + "'"
159 os.execl(git_shell_cmd, git_shell_cmd, '-c', cmd)
160 elif action == 'set-keywords':
161 if len(cmdargv) < 2:
162 die_with_help("{:s}: missing repository name".format(action))
163 pkgbase_set_keywords(cmdargv[1], cmdargv[2:])
164 elif action == 'list-repos':
165 if len(cmdargv) > 1:
166 die_with_help("{:s}: too many arguments".format(action))
167 list_repos(user)
168 elif action == 'setup-repo':
169 if len(cmdargv) < 2:
170 die_with_help("{:s}: missing repository name".format(action))
171 if len(cmdargv) > 2:
172 die_with_help("{:s}: too many arguments".format(action))
173 warn('{:s} is deprecated. Use `git push` to create new repositories.'.format(action))
174 create_pkgbase(cmdargv[1], user)
175 elif action == 'restore':
176 if len(cmdargv) < 2:
177 die_with_help("{:s}: missing repository name".format(action))
178 if len(cmdargv) > 2:
179 die_with_help("{:s}: too many arguments".format(action))
181 pkgbase = cmdargv[1]
182 if not re.match(repo_regex, pkgbase):
183 die('{:s}: invalid repository name: {:s}'.format(action, pkgbase))
185 if pkgbase_exists(pkgbase):
186 die('{:s}: package base exists: {:s}'.format(action, pkgbase))
187 create_pkgbase(pkgbase, user)
189 os.environ["AUR_USER"] = user
190 os.environ["AUR_PKGBASE"] = pkgbase
191 os.execl(git_update_cmd, git_update_cmd, 'restore')
192 elif action == 'help':
193 die("Commands:\n" +
194 " help Show this help message and exit.\n" +
195 " list-repos List all your repositories.\n" +
196 " restore <name> Restore a deleted package base.\n" +
197 " set-keywords <name> [...] Change package base keywords.\n" +
198 " setup-repo <name> Create an empty repository.\n" +
199 " git-receive-pack Internal command used with Git.\n" +
200 " git-upload-pack Internal command used with Git.")
201 else:
202 die_with_help("invalid command: {:s}".format(action))