Linux server.edchosting.com 4.18.0-553.79.1.lve.el7h.x86_64 #1 SMP Wed Oct 15 16:34:46 UTC 2025 x86_64
LiteSpeed
Server IP : 75.98.162.185 & Your IP : 216.73.216.163
Domains :
Cant Read [ /etc/named.conf ]
User : goons4good
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
/
lib64 /
python2.7 /
site-packages /
mercurial /
Delete
Unzip
Name
Size
Permission
Date
Action
help
[ DIR ]
drwxr-xr-x
2021-09-16 10:54
hgweb
[ DIR ]
drwxr-xr-x
2021-09-16 10:54
httpclient
[ DIR ]
drwxr-xr-x
2021-09-16 10:54
templates
[ DIR ]
drwxr-xr-x
2021-09-16 10:54
__init__.py
0
B
-rw-r--r--
2013-06-01 18:10
__init__.pyc
143
B
-rw-r--r--
2020-03-31 23:33
__init__.pyo
143
B
-rw-r--r--
2020-03-31 23:33
__version__.py
59
B
-rw-r--r--
2013-06-01 18:10
__version__.pyc
174
B
-rw-r--r--
2020-03-31 23:33
__version__.pyo
174
B
-rw-r--r--
2020-03-31 23:33
ancestor.py
12.62
KB
-rw-r--r--
2013-06-01 18:10
ancestor.pyc
9.1
KB
-rw-r--r--
2020-03-31 23:33
ancestor.pyo
9.1
KB
-rw-r--r--
2020-03-31 23:33
archival.py
10.15
KB
-rw-r--r--
2013-06-01 18:10
archival.pyc
12.62
KB
-rw-r--r--
2020-03-31 23:33
archival.pyo
12.62
KB
-rw-r--r--
2020-03-31 23:33
base85.so
11.05
KB
-rwxr-xr-x
2020-03-31 23:33
bdiff.so
15.23
KB
-rwxr-xr-x
2020-03-31 23:33
bookmarks.py
9.44
KB
-rw-r--r--
2013-06-01 18:10
bookmarks.pyc
9.4
KB
-rw-r--r--
2020-03-31 23:33
bookmarks.pyo
9.4
KB
-rw-r--r--
2020-03-31 23:33
branchmap.py
8.74
KB
-rw-r--r--
2013-06-01 18:10
branchmap.pyc
7.53
KB
-rw-r--r--
2020-03-31 23:33
branchmap.pyo
7.48
KB
-rw-r--r--
2020-03-31 23:33
bundlerepo.py
13.38
KB
-rw-r--r--
2013-06-01 18:10
bundlerepo.pyc
13.36
KB
-rw-r--r--
2020-03-31 23:33
bundlerepo.pyo
13.36
KB
-rw-r--r--
2020-03-31 23:33
byterange.py
16.14
KB
-rw-r--r--
2013-06-01 18:10
byterange.pyc
14.98
KB
-rw-r--r--
2020-03-31 23:33
byterange.pyo
14.83
KB
-rw-r--r--
2020-03-31 23:33
changegroup.py
8.09
KB
-rw-r--r--
2013-06-01 18:10
changegroup.pyc
11.1
KB
-rw-r--r--
2020-03-31 23:33
changegroup.pyo
11.1
KB
-rw-r--r--
2020-03-31 23:33
changelog.py
11.75
KB
-rw-r--r--
2013-06-01 18:10
changelog.pyc
14.08
KB
-rw-r--r--
2020-03-31 23:33
changelog.pyo
14.04
KB
-rw-r--r--
2020-03-31 23:33
cmdutil.py
75.63
KB
-rw-r--r--
2020-03-31 23:33
cmdutil.pyc
64.09
KB
-rw-r--r--
2020-03-31 23:33
cmdutil.pyo
64.09
KB
-rw-r--r--
2020-03-31 23:33
commands.py
211.62
KB
-rw-r--r--
2013-06-01 18:10
commands.pyc
197.58
KB
-rw-r--r--
2020-03-31 23:33
commands.pyo
197.53
KB
-rw-r--r--
2020-03-31 23:33
commandserver.py
6.56
KB
-rw-r--r--
2013-06-01 18:10
commandserver.pyc
7.48
KB
-rw-r--r--
2020-03-31 23:33
commandserver.pyo
7.44
KB
-rw-r--r--
2020-03-31 23:33
config.py
6.57
KB
-rw-r--r--
2013-06-01 18:10
config.pyc
8.15
KB
-rw-r--r--
2020-03-31 23:33
config.pyo
8.15
KB
-rw-r--r--
2020-03-31 23:33
context.py
44.46
KB
-rw-r--r--
2020-03-31 23:33
context.pyc
59.66
KB
-rw-r--r--
2020-03-31 23:33
context.pyo
59.43
KB
-rw-r--r--
2020-03-31 23:33
copies.py
12.04
KB
-rw-r--r--
2013-06-01 18:10
copies.pyc
9.19
KB
-rw-r--r--
2020-03-31 23:33
copies.pyo
9.19
KB
-rw-r--r--
2020-03-31 23:33
dagparser.py
14.4
KB
-rw-r--r--
2013-06-01 18:10
dagparser.pyc
12.92
KB
-rw-r--r--
2020-03-31 23:33
dagparser.pyo
12.92
KB
-rw-r--r--
2020-03-31 23:33
dagutil.py
8.04
KB
-rw-r--r--
2013-06-01 18:10
dagutil.pyc
11.31
KB
-rw-r--r--
2020-03-31 23:33
dagutil.pyo
11.21
KB
-rw-r--r--
2020-03-31 23:33
demandimport.py
5.13
KB
-rw-r--r--
2013-06-01 18:10
demandimport.pyc
5.17
KB
-rw-r--r--
2020-03-31 23:33
demandimport.pyo
5.17
KB
-rw-r--r--
2020-03-31 23:33
dicthelpers.py
1.58
KB
-rw-r--r--
2013-06-01 18:10
dicthelpers.pyc
1.59
KB
-rw-r--r--
2020-03-31 23:33
dicthelpers.pyo
1.59
KB
-rw-r--r--
2020-03-31 23:33
diffhelpers.so
11.18
KB
-rwxr-xr-x
2020-03-31 23:33
dirstate.py
27.42
KB
-rw-r--r--
2020-03-31 23:33
dirstate.pyc
25.63
KB
-rw-r--r--
2020-03-31 23:33
dirstate.pyo
25.63
KB
-rw-r--r--
2020-03-31 23:33
discovery.py
13.38
KB
-rw-r--r--
2013-06-01 18:10
discovery.pyc
11.35
KB
-rw-r--r--
2020-03-31 23:33
discovery.pyo
11.35
KB
-rw-r--r--
2020-03-31 23:33
dispatch.py
31.68
KB
-rw-r--r--
2020-03-31 23:33
dispatch.pyc
27.9
KB
-rw-r--r--
2020-03-31 23:33
dispatch.pyo
27.9
KB
-rw-r--r--
2020-03-31 23:33
encoding.py
9.36
KB
-rw-r--r--
2013-06-01 18:10
encoding.pyc
9.96
KB
-rw-r--r--
2020-03-31 23:33
encoding.pyo
9.96
KB
-rw-r--r--
2020-03-31 23:33
error.py
2.68
KB
-rw-r--r--
2013-06-01 18:10
error.pyc
6.36
KB
-rw-r--r--
2020-03-31 23:33
error.pyo
6.36
KB
-rw-r--r--
2020-03-31 23:33
extensions.py
10.69
KB
-rw-r--r--
2013-06-01 18:10
extensions.pyc
11.3
KB
-rw-r--r--
2020-03-31 23:33
extensions.pyo
11.15
KB
-rw-r--r--
2020-03-31 23:33
fancyopts.py
3.48
KB
-rw-r--r--
2013-06-01 18:10
fancyopts.pyc
2.95
KB
-rw-r--r--
2020-03-31 23:33
fancyopts.pyo
2.95
KB
-rw-r--r--
2020-03-31 23:33
filelog.py
2.69
KB
-rw-r--r--
2013-06-01 18:10
filelog.pyc
3.59
KB
-rw-r--r--
2020-03-31 23:33
filelog.pyo
3.59
KB
-rw-r--r--
2020-03-31 23:33
filemerge.py
12.55
KB
-rw-r--r--
2013-06-01 18:10
filemerge.pyc
13.13
KB
-rw-r--r--
2020-03-31 23:33
filemerge.pyo
13.13
KB
-rw-r--r--
2020-03-31 23:33
fileset.py
14.92
KB
-rw-r--r--
2013-06-01 18:10
fileset.pyc
20.04
KB
-rw-r--r--
2020-03-31 23:33
fileset.pyo
20.04
KB
-rw-r--r--
2020-03-31 23:33
formatter.py
2.79
KB
-rw-r--r--
2013-06-01 18:10
formatter.pyc
5.5
KB
-rw-r--r--
2020-03-31 23:33
formatter.pyo
5.5
KB
-rw-r--r--
2020-03-31 23:33
graphmod.py
11.79
KB
-rw-r--r--
2013-06-01 18:10
graphmod.pyc
10.08
KB
-rw-r--r--
2020-03-31 23:33
graphmod.pyo
10.02
KB
-rw-r--r--
2020-03-31 23:33
hbisect.py
9.01
KB
-rw-r--r--
2013-06-01 18:10
hbisect.pyc
6.59
KB
-rw-r--r--
2020-03-31 23:33
hbisect.pyo
6.55
KB
-rw-r--r--
2020-03-31 23:33
help.py
17.6
KB
-rw-r--r--
2013-06-01 18:10
help.pyc
16.02
KB
-rw-r--r--
2020-03-31 23:33
help.pyo
16.02
KB
-rw-r--r--
2020-03-31 23:33
hg.py
21.44
KB
-rw-r--r--
2013-06-01 18:10
hg.pyc
20.09
KB
-rw-r--r--
2020-03-31 23:33
hg.pyo
20.09
KB
-rw-r--r--
2020-03-31 23:33
hook.py
7.7
KB
-rw-r--r--
2013-06-01 18:10
hook.pyc
6.56
KB
-rw-r--r--
2020-03-31 23:33
hook.pyo
6.56
KB
-rw-r--r--
2020-03-31 23:33
httpconnection.py
10.66
KB
-rw-r--r--
2013-06-01 18:10
httpconnection.pyc
9.32
KB
-rw-r--r--
2020-03-31 23:33
httpconnection.pyo
9.32
KB
-rw-r--r--
2020-03-31 23:33
httppeer.py
9.08
KB
-rw-r--r--
2013-06-01 18:10
httppeer.pyc
9.36
KB
-rw-r--r--
2020-03-31 23:33
httppeer.pyo
9.36
KB
-rw-r--r--
2020-03-31 23:33
i18n.py
2.1
KB
-rw-r--r--
2020-03-31 23:33
i18n.pyc
1.86
KB
-rw-r--r--
2020-03-31 23:33
i18n.pyo
1.86
KB
-rw-r--r--
2020-03-31 23:33
ignore.py
3.48
KB
-rw-r--r--
2013-06-01 18:10
ignore.pyc
3.25
KB
-rw-r--r--
2020-03-31 23:33
ignore.pyo
3.25
KB
-rw-r--r--
2020-03-31 23:33
keepalive.py
25.31
KB
-rw-r--r--
2013-06-01 18:10
keepalive.pyc
21.97
KB
-rw-r--r--
2020-03-31 23:33
keepalive.pyo
21.97
KB
-rw-r--r--
2020-03-31 23:33
localrepo.py
99.89
KB
-rw-r--r--
2020-03-31 23:33
localrepo.pyc
83.39
KB
-rw-r--r--
2020-03-31 23:33
localrepo.pyo
83.34
KB
-rw-r--r--
2020-03-31 23:33
lock.py
4.81
KB
-rw-r--r--
2013-06-01 18:10
lock.pyc
4.57
KB
-rw-r--r--
2020-03-31 23:33
lock.pyo
4.57
KB
-rw-r--r--
2020-03-31 23:33
lsprof.py
3.59
KB
-rw-r--r--
2013-06-01 18:10
lsprof.pyc
4.47
KB
-rw-r--r--
2020-03-31 23:33
lsprof.pyo
4.47
KB
-rw-r--r--
2020-03-31 23:33
lsprofcalltree.py
2.7
KB
-rw-r--r--
2013-06-01 18:10
lsprofcalltree.pyc
2.99
KB
-rw-r--r--
2020-03-31 23:33
lsprofcalltree.pyo
2.99
KB
-rw-r--r--
2020-03-31 23:33
mail.py
12.21
KB
-rw-r--r--
2013-06-01 18:10
mail.pyc
13.48
KB
-rw-r--r--
2020-03-31 23:33
mail.pyo
13.48
KB
-rw-r--r--
2020-03-31 23:33
manifest.py
7.95
KB
-rw-r--r--
2013-06-01 18:10
manifest.pyc
8.03
KB
-rw-r--r--
2020-03-31 23:33
manifest.pyo
8.03
KB
-rw-r--r--
2020-03-31 23:33
match.py
10.73
KB
-rw-r--r--
2013-06-01 18:10
match.pyc
13.96
KB
-rw-r--r--
2020-03-31 23:33
match.pyo
13.96
KB
-rw-r--r--
2020-03-31 23:33
mdiff.py
11.34
KB
-rw-r--r--
2013-06-01 18:10
mdiff.pyc
11.04
KB
-rw-r--r--
2020-03-31 23:33
mdiff.pyo
11.04
KB
-rw-r--r--
2020-03-31 23:33
merge.py
27.73
KB
-rw-r--r--
2013-06-01 18:10
merge.pyc
23.22
KB
-rw-r--r--
2020-03-31 23:33
merge.pyo
23.12
KB
-rw-r--r--
2020-03-31 23:33
minirst.py
23.93
KB
-rw-r--r--
2013-06-01 18:10
minirst.pyc
20.82
KB
-rw-r--r--
2020-03-31 23:33
minirst.pyo
20.82
KB
-rw-r--r--
2020-03-31 23:33
mpatch.so
11.17
KB
-rwxr-xr-x
2020-03-31 23:33
node.py
449
B
-rw-r--r--
2013-06-01 18:10
node.pyc
483
B
-rw-r--r--
2020-03-31 23:33
node.pyo
483
B
-rw-r--r--
2020-03-31 23:33
obsolete.py
29.41
KB
-rw-r--r--
2013-06-01 18:10
obsolete.pyc
25.01
KB
-rw-r--r--
2020-03-31 23:33
obsolete.pyo
24.97
KB
-rw-r--r--
2020-03-31 23:33
osutil.so
16.07
KB
-rwxr-xr-x
2020-03-31 23:33
parser.py
3.67
KB
-rw-r--r--
2013-06-01 18:10
parser.pyc
3.32
KB
-rw-r--r--
2020-03-31 23:33
parser.pyo
3.32
KB
-rw-r--r--
2020-03-31 23:33
parsers.so
45.55
KB
-rwxr-xr-x
2020-03-31 23:33
patch.py
63.88
KB
-rw-r--r--
2013-06-01 18:10
patch.pyc
59.85
KB
-rw-r--r--
2020-03-31 23:33
patch.pyo
59.85
KB
-rw-r--r--
2020-03-31 23:33
peer.py
1.32
KB
-rw-r--r--
2013-06-01 18:10
peer.pyc
1.96
KB
-rw-r--r--
2020-03-31 23:33
peer.pyo
1.96
KB
-rw-r--r--
2020-03-31 23:33
phases.py
14.37
KB
-rw-r--r--
2013-06-01 18:10
phases.pyc
13.55
KB
-rw-r--r--
2020-03-31 23:33
phases.pyo
13.55
KB
-rw-r--r--
2020-03-31 23:33
posix.py
18.95
KB
-rw-r--r--
2020-03-31 23:33
posix.pyc
21.35
KB
-rw-r--r--
2020-03-31 23:33
posix.pyo
21.35
KB
-rw-r--r--
2020-03-31 23:33
pushkey.py
1.13
KB
-rw-r--r--
2013-06-01 18:10
pushkey.pyc
1.99
KB
-rw-r--r--
2020-03-31 23:33
pushkey.pyo
1.99
KB
-rw-r--r--
2020-03-31 23:33
pvec.py
5.85
KB
-rw-r--r--
2013-06-01 18:10
pvec.pyc
7.36
KB
-rw-r--r--
2020-03-31 23:33
pvec.pyo
7.36
KB
-rw-r--r--
2020-03-31 23:33
py3kcompat.py
2.31
KB
-rw-r--r--
2013-06-01 18:10
py3kcompat.pyc
1.9
KB
-rw-r--r--
2020-03-31 23:33
py3kcompat.pyo
1.9
KB
-rw-r--r--
2020-03-31 23:33
repair.py
6.02
KB
-rw-r--r--
2013-06-01 18:10
repair.pyc
5.12
KB
-rw-r--r--
2020-03-31 23:33
repair.pyo
5.12
KB
-rw-r--r--
2020-03-31 23:33
repoview.py
8.49
KB
-rw-r--r--
2013-06-01 18:10
repoview.pyc
9.2
KB
-rw-r--r--
2020-03-31 23:33
repoview.pyo
9.06
KB
-rw-r--r--
2020-03-31 23:33
revlog.py
44.66
KB
-rw-r--r--
2013-06-01 18:10
revlog.pyc
38.92
KB
-rw-r--r--
2020-03-31 23:33
revlog.pyo
38.86
KB
-rw-r--r--
2020-03-31 23:33
revset.py
61.17
KB
-rw-r--r--
2013-06-01 18:10
revset.pyc
67.28
KB
-rw-r--r--
2020-03-31 23:33
revset.pyo
67.28
KB
-rw-r--r--
2020-03-31 23:33
scmposix.py
877
B
-rw-r--r--
2013-06-01 18:10
scmposix.pyc
1.43
KB
-rw-r--r--
2020-03-31 23:33
scmposix.pyo
1.43
KB
-rw-r--r--
2020-03-31 23:33
scmutil.py
31.78
KB
-rw-r--r--
2020-03-31 23:33
scmutil.pyc
35.55
KB
-rw-r--r--
2020-03-31 23:33
scmutil.pyo
35.45
KB
-rw-r--r--
2020-03-31 23:33
scmwindows.py
1.63
KB
-rw-r--r--
2013-06-01 18:10
scmwindows.pyc
1.79
KB
-rw-r--r--
2020-03-31 23:33
scmwindows.pyo
1.79
KB
-rw-r--r--
2020-03-31 23:33
setdiscovery.py
6.73
KB
-rw-r--r--
2013-06-01 18:10
setdiscovery.pyc
5.2
KB
-rw-r--r--
2020-03-31 23:33
setdiscovery.pyo
5.17
KB
-rw-r--r--
2020-03-31 23:33
similar.py
3.56
KB
-rw-r--r--
2013-06-01 18:10
similar.pyc
3.32
KB
-rw-r--r--
2020-03-31 23:33
similar.pyo
3.32
KB
-rw-r--r--
2020-03-31 23:33
simplemerge.py
14.93
KB
-rw-r--r--
2013-06-01 18:10
simplemerge.pyc
12.2
KB
-rw-r--r--
2020-03-31 23:33
simplemerge.pyo
11.77
KB
-rw-r--r--
2020-03-31 23:33
sshpeer.py
7.22
KB
-rw-r--r--
2020-03-31 23:33
sshpeer.pyc
8.78
KB
-rw-r--r--
2020-03-31 23:33
sshpeer.pyo
8.78
KB
-rw-r--r--
2020-03-31 23:33
sshserver.py
4.17
KB
-rw-r--r--
2013-06-01 18:10
sshserver.pyc
5.77
KB
-rw-r--r--
2020-03-31 23:33
sshserver.pyo
5.77
KB
-rw-r--r--
2020-03-31 23:33
sslutil.py
6.49
KB
-rw-r--r--
2013-06-01 18:10
sslutil.pyc
5.57
KB
-rw-r--r--
2020-03-31 23:33
sslutil.pyo
5.57
KB
-rw-r--r--
2020-03-31 23:33
statichttprepo.py
5.05
KB
-rw-r--r--
2013-06-01 18:10
statichttprepo.pyc
7.04
KB
-rw-r--r--
2020-03-31 23:33
statichttprepo.pyo
7.04
KB
-rw-r--r--
2020-03-31 23:33
store.py
16.47
KB
-rw-r--r--
2013-06-01 18:10
store.pyc
20.56
KB
-rw-r--r--
2020-03-31 23:33
store.pyo
20.56
KB
-rw-r--r--
2020-03-31 23:33
strutil.py
913
B
-rw-r--r--
2013-06-01 18:10
strutil.pyc
978
B
-rw-r--r--
2020-03-31 23:33
strutil.pyo
978
B
-rw-r--r--
2020-03-31 23:33
subrepo.py
54.68
KB
-rw-r--r--
2020-03-31 23:33
subrepo.pyc
52.89
KB
-rw-r--r--
2020-03-31 23:33
subrepo.pyo
52.89
KB
-rw-r--r--
2020-03-31 23:33
tags.py
11.22
KB
-rw-r--r--
2013-06-01 18:10
tags.pyc
6.89
KB
-rw-r--r--
2020-03-31 23:33
tags.pyo
6.39
KB
-rw-r--r--
2020-03-31 23:33
templatefilters.py
12.66
KB
-rw-r--r--
2013-06-01 18:10
templatefilters.pyc
17.98
KB
-rw-r--r--
2020-03-31 23:33
templatefilters.pyo
17.98
KB
-rw-r--r--
2020-03-31 23:33
templatekw.py
12.92
KB
-rw-r--r--
2013-06-01 18:10
templatekw.pyc
16.5
KB
-rw-r--r--
2020-03-31 23:33
templatekw.pyo
16.5
KB
-rw-r--r--
2020-03-31 23:33
templater.py
16.28
KB
-rw-r--r--
2013-06-01 18:10
templater.pyc
18.12
KB
-rw-r--r--
2020-03-31 23:33
templater.pyo
18.12
KB
-rw-r--r--
2020-03-31 23:33
transaction.py
5.09
KB
-rw-r--r--
2013-06-01 18:10
transaction.pyc
6.34
KB
-rw-r--r--
2020-03-31 23:33
transaction.pyo
6.34
KB
-rw-r--r--
2020-03-31 23:33
treediscovery.py
5.05
KB
-rw-r--r--
2013-06-01 18:10
treediscovery.pyc
3.67
KB
-rw-r--r--
2020-03-31 23:33
treediscovery.pyo
3.67
KB
-rw-r--r--
2020-03-31 23:33
ui.py
29.33
KB
-rw-r--r--
2013-06-01 18:10
ui.pyc
28.88
KB
-rw-r--r--
2020-03-31 23:33
ui.pyo
28.88
KB
-rw-r--r--
2020-03-31 23:33
unionrepo.py
7.27
KB
-rw-r--r--
2013-06-01 18:10
unionrepo.pyc
8.85
KB
-rw-r--r--
2020-03-31 23:33
unionrepo.pyo
8.82
KB
-rw-r--r--
2020-03-31 23:33
url.py
17.08
KB
-rw-r--r--
2013-06-01 18:10
url.pyc
15.91
KB
-rw-r--r--
2020-03-31 23:33
url.pyo
15.91
KB
-rw-r--r--
2020-03-31 23:33
util.py
60.48
KB
-rw-r--r--
2020-03-31 23:33
util.pyc
63.22
KB
-rw-r--r--
2020-03-31 23:33
util.pyo
63.14
KB
-rw-r--r--
2020-03-31 23:33
verify.py
10.68
KB
-rw-r--r--
2013-06-01 18:10
verify.pyc
9.37
KB
-rw-r--r--
2020-03-31 23:33
verify.pyo
9.37
KB
-rw-r--r--
2020-03-31 23:33
win32.py
13.64
KB
-rw-r--r--
2013-06-01 18:10
win32.pyc
11.5
KB
-rw-r--r--
2020-03-31 23:33
win32.pyo
11.5
KB
-rw-r--r--
2020-03-31 23:33
windows.py
10.76
KB
-rw-r--r--
2020-03-31 23:33
windows.pyc
14.12
KB
-rw-r--r--
2020-03-31 23:33
windows.pyo
14.12
KB
-rw-r--r--
2020-03-31 23:33
wireproto.py
22.98
KB
-rw-r--r--
2020-03-31 23:33
wireproto.pyc
27.79
KB
-rw-r--r--
2020-03-31 23:33
wireproto.pyo
27.79
KB
-rw-r--r--
2020-03-31 23:33
worker.py
4.4
KB
-rw-r--r--
2013-06-01 18:10
worker.pyc
5.31
KB
-rw-r--r--
2020-03-31 23:33
worker.pyo
5.31
KB
-rw-r--r--
2020-03-31 23:33
Save
Rename
# wireproto.py - generic wire protocol support functions # # Copyright 2005-2010 Matt Mackall <mpm@selenic.com> # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. import urllib, tempfile, os, sys from i18n import _ from node import bin, hex import changegroup as changegroupmod import peer, error, encoding, util, store # Maps wire protocol name to operation type. This is used for permissions # checking. permissions = {} # abstract batching support class future(object): '''placeholder for a value to be set later''' def set(self, value): if util.safehasattr(self, 'value'): raise error.RepoError("future is already set") self.value = value class batcher(object): '''base class for batches of commands submittable in a single request All methods invoked on instances of this class are simply queued and return a a future for the result. Once you call submit(), all the queued calls are performed and the results set in their respective futures. ''' def __init__(self): self.calls = [] def __getattr__(self, name): def call(*args, **opts): resref = future() self.calls.append((name, args, opts, resref,)) return resref return call def submit(self): pass class localbatch(batcher): '''performs the queued calls directly''' def __init__(self, local): batcher.__init__(self) self.local = local def submit(self): for name, args, opts, resref in self.calls: resref.set(getattr(self.local, name)(*args, **opts)) class remotebatch(batcher): '''batches the queued calls; uses as few roundtrips as possible''' def __init__(self, remote): '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)''' batcher.__init__(self) self.remote = remote def submit(self): req, rsp = [], [] for name, args, opts, resref in self.calls: mtd = getattr(self.remote, name) batchablefn = getattr(mtd, 'batchable', None) if batchablefn is not None: batchable = batchablefn(mtd.im_self, *args, **opts) encargsorres, encresref = batchable.next() if encresref: req.append((name, encargsorres,)) rsp.append((batchable, encresref, resref,)) else: resref.set(encargsorres) else: if req: self._submitreq(req, rsp) req, rsp = [], [] resref.set(mtd(*args, **opts)) if req: self._submitreq(req, rsp) def _submitreq(self, req, rsp): encresults = self.remote._submitbatch(req) for encres, r in zip(encresults, rsp): batchable, encresref, resref = r encresref.set(encres) resref.set(batchable.next()) def batchable(f): '''annotation for batchable methods Such methods must implement a coroutine as follows: @batchable def sample(self, one, two=None): # Handle locally computable results first: if not one: yield "a local result", None # Build list of encoded arguments suitable for your wire protocol: encargs = [('one', encode(one),), ('two', encode(two),)] # Create future for injection of encoded result: encresref = future() # Return encoded arguments and future: yield encargs, encresref # Assuming the future to be filled with the result from the batched # request now. Decode it: yield decode(encresref.value) The decorator returns a function which wraps this coroutine as a plain method, but adds the original method as an attribute called "batchable", which is used by remotebatch to split the call into separate encoding and decoding phases. ''' def plain(*args, **opts): batchable = f(*args, **opts) encargsorres, encresref = batchable.next() if not encresref: return encargsorres # a local result in this case self = args[0] encresref.set(self._submitone(f.func_name, encargsorres)) return batchable.next() setattr(plain, 'batchable', f) return plain # list of nodes encoding / decoding def decodelist(l, sep=' '): if l: return map(bin, l.split(sep)) return [] def encodelist(l, sep=' '): return sep.join(map(hex, l)) # batched call argument encoding def escapearg(plain): return (plain .replace(':', '::') .replace(',', ':,') .replace(';', ':;') .replace('=', ':=')) def unescapearg(escaped): return (escaped .replace(':=', '=') .replace(':;', ';') .replace(':,', ',') .replace('::', ':')) # client side def todict(**args): return args class wirepeer(peer.peerrepository): def batch(self): return remotebatch(self) def _submitbatch(self, req): cmds = [] for op, argsdict in req: args = ','.join('%s=%s' % p for p in argsdict.iteritems()) cmds.append('%s %s' % (op, args)) rsp = self._call("batch", cmds=';'.join(cmds)) return rsp.split(';') def _submitone(self, op, args): return self._call(op, **args) @batchable def lookup(self, key): self.requirecap('lookup', _('look up remote revision')) f = future() yield todict(key=encoding.fromlocal(key)), f d = f.value success, data = d[:-1].split(" ", 1) if int(success): yield bin(data) self._abort(error.RepoError(data)) @batchable def heads(self): f = future() yield {}, f d = f.value try: yield decodelist(d[:-1]) except ValueError: self._abort(error.ResponseError(_("unexpected response:"), d)) @batchable def known(self, nodes): f = future() yield todict(nodes=encodelist(nodes)), f d = f.value try: yield [bool(int(f)) for f in d] except ValueError: self._abort(error.ResponseError(_("unexpected response:"), d)) @batchable def branchmap(self): f = future() yield {}, f d = f.value try: branchmap = {} for branchpart in d.splitlines(): branchname, branchheads = branchpart.split(' ', 1) branchname = encoding.tolocal(urllib.unquote(branchname)) branchheads = decodelist(branchheads) branchmap[branchname] = branchheads yield branchmap except TypeError: self._abort(error.ResponseError(_("unexpected response:"), d)) def branches(self, nodes): n = encodelist(nodes) d = self._call("branches", nodes=n) try: br = [tuple(decodelist(b)) for b in d.splitlines()] return br except ValueError: self._abort(error.ResponseError(_("unexpected response:"), d)) def between(self, pairs): batch = 8 # avoid giant requests r = [] for i in xrange(0, len(pairs), batch): n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]]) d = self._call("between", pairs=n) try: r.extend(l and decodelist(l) or [] for l in d.splitlines()) except ValueError: self._abort(error.ResponseError(_("unexpected response:"), d)) return r @batchable def pushkey(self, namespace, key, old, new): if not self.capable('pushkey'): yield False, None f = future() self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key)) yield todict(namespace=encoding.fromlocal(namespace), key=encoding.fromlocal(key), old=encoding.fromlocal(old), new=encoding.fromlocal(new)), f d = f.value d, output = d.split('\n', 1) try: d = bool(int(d)) except ValueError: raise error.ResponseError( _('push failed (unexpected response):'), d) for l in output.splitlines(True): self.ui.status(_('remote: '), l) yield d @batchable def listkeys(self, namespace): if not self.capable('pushkey'): yield {}, None f = future() self.ui.debug('preparing listkeys for "%s"\n' % namespace) yield todict(namespace=encoding.fromlocal(namespace)), f d = f.value r = {} for l in d.splitlines(): k, v = l.split('\t') r[encoding.tolocal(k)] = encoding.tolocal(v) yield r def stream_out(self): return self._callstream('stream_out') def changegroup(self, nodes, kind): n = encodelist(nodes) f = self._callstream("changegroup", roots=n) return changegroupmod.unbundle10(self._decompress(f), 'UN') def changegroupsubset(self, bases, heads, kind): self.requirecap('changegroupsubset', _('look up remote changes')) bases = encodelist(bases) heads = encodelist(heads) f = self._callstream("changegroupsubset", bases=bases, heads=heads) return changegroupmod.unbundle10(self._decompress(f), 'UN') def getbundle(self, source, heads=None, common=None): self.requirecap('getbundle', _('look up remote changes')) opts = {} if heads is not None: opts['heads'] = encodelist(heads) if common is not None: opts['common'] = encodelist(common) f = self._callstream("getbundle", **opts) return changegroupmod.unbundle10(self._decompress(f), 'UN') def unbundle(self, cg, heads, source): '''Send cg (a readable file-like object representing the changegroup to push, typically a chunkbuffer object) to the remote server as a bundle. Return an integer indicating the result of the push (see localrepository.addchangegroup()).''' if heads != ['force'] and self.capable('unbundlehash'): heads = encodelist(['hashed', util.sha1(''.join(sorted(heads))).digest()]) else: heads = encodelist(heads) ret, output = self._callpush("unbundle", cg, heads=heads) if ret == "": raise error.ResponseError( _('push failed:'), output) try: ret = int(ret) except ValueError: raise error.ResponseError( _('push failed (unexpected response):'), ret) for l in output.splitlines(True): self.ui.status(_('remote: '), l) return ret def debugwireargs(self, one, two, three=None, four=None, five=None): # don't pass optional arguments left at their default value opts = {} if three is not None: opts['three'] = three if four is not None: opts['four'] = four return self._call('debugwireargs', one=one, two=two, **opts) # server side class streamres(object): def __init__(self, gen): self.gen = gen class pushres(object): def __init__(self, res): self.res = res class pusherr(object): def __init__(self, res): self.res = res class ooberror(object): def __init__(self, message): self.message = message def dispatch(repo, proto, command): repo = repo.filtered("served") func, spec = commands[command] args = proto.getargs(spec) return func(repo, proto, *args) def options(cmd, keys, others): opts = {} for k in keys: if k in others: opts[k] = others[k] del others[k] if others: sys.stderr.write("abort: %s got unexpected arguments %s\n" % (cmd, ",".join(others))) return opts def wireprotocommand(name, args=''): """decorator for wire protocol command""" def register(func): commands[name] = (func, args) return func return register # TODO define a more appropriate permissions type to use for this. permissions['batch'] = 'pull' def batch(repo, proto, cmds, others): repo = repo.filtered("served") res = [] for pair in cmds.split(';'): op, args = pair.split(' ', 1) vals = {} for a in args.split(','): if a: n, v = a.split('=') vals[n] = unescapearg(v) func, spec = commands[op] # If the protocol supports permissions checking, perform that # checking on each batched command. # TODO formalize permission checking as part of protocol interface. if util.safehasattr(proto, 'checkperm'): # Assume commands with no defined permissions are writes / for # pushes. This is the safest from a security perspective because # it doesn't allow commands with undefined semantics from # bypassing permissions checks. proto.checkperm(permissions.get(op, 'push')) if spec: keys = spec.split() data = {} for k in keys: if k == '*': star = {} for key in vals.keys(): if key not in keys: star[key] = vals[key] data['*'] = star else: data[k] = vals[k] result = func(repo, proto, *[data[k] for k in keys]) else: result = func(repo, proto) if isinstance(result, ooberror): return result res.append(escapearg(result)) return ';'.join(res) permissions['between'] = 'pull' def between(repo, proto, pairs): pairs = [decodelist(p, '-') for p in pairs.split(" ")] r = [] for b in repo.between(pairs): r.append(encodelist(b) + "\n") return "".join(r) permissions['branchmap'] = 'pull' def branchmap(repo, proto): branchmap = repo.branchmap() heads = [] for branch, nodes in branchmap.iteritems(): branchname = urllib.quote(encoding.fromlocal(branch)) branchnodes = encodelist(nodes) heads.append('%s %s' % (branchname, branchnodes)) return '\n'.join(heads) permissions['branches'] = 'pull' def branches(repo, proto, nodes): nodes = decodelist(nodes) r = [] for b in repo.branches(nodes): r.append(encodelist(b) + "\n") return "".join(r) permissions['capabilities'] = 'pull' def capabilities(repo, proto): caps = ('lookup changegroupsubset branchmap pushkey known getbundle ' 'unbundlehash batch').split() if _allowstream(repo.ui): if repo.ui.configbool('server', 'preferuncompressed', False): caps.append('stream-preferred') requiredformats = repo.requirements & repo.supportedformats # if our local revlogs are just revlogv1, add 'stream' cap if not requiredformats - set(('revlogv1',)): caps.append('stream') # otherwise, add 'streamreqs' detailing our local revlog format else: caps.append('streamreqs=%s' % ','.join(requiredformats)) caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority)) caps.append('httpheader=1024') return ' '.join(caps) permissions['changegroup'] = 'pull' def changegroup(repo, proto, roots): nodes = decodelist(roots) cg = repo.changegroup(nodes, 'serve') return streamres(proto.groupchunks(cg)) permissions['changegroupsubset'] = 'pull' def changegroupsubset(repo, proto, bases, heads): bases = decodelist(bases) heads = decodelist(heads) cg = repo.changegroupsubset(bases, heads, 'serve') return streamres(proto.groupchunks(cg)) permissions['debugwireargs'] = 'pull' def debugwireargs(repo, proto, one, two, others): # only accept optional args from the known set opts = options('debugwireargs', ['three', 'four'], others) return repo.debugwireargs(one, two, **opts) permissions['getbundle'] = 'pull' def getbundle(repo, proto, others): opts = options('getbundle', ['heads', 'common'], others) for k, v in opts.iteritems(): opts[k] = decodelist(v) cg = repo.getbundle('serve', **opts) return streamres(proto.groupchunks(cg)) permissions['heads'] = 'pull' def heads(repo, proto): h = repo.heads() return encodelist(h) + "\n" permissions['hello'] = 'pull' def hello(repo, proto): '''the hello command returns a set of lines describing various interesting things about the server, in an RFC822-like format. Currently the only one defined is "capabilities", which consists of a line in the form: capabilities: space separated list of tokens ''' return "capabilities: %s\n" % (capabilities(repo, proto)) permissions['listkeys'] = 'pull' def listkeys(repo, proto, namespace): d = repo.listkeys(encoding.tolocal(namespace)).items() t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v)) for k, v in d]) return t permissions['lookup'] = 'pull' def lookup(repo, proto, key): try: k = encoding.tolocal(key) c = repo[k] r = c.hex() success = 1 except Exception, inst: r = str(inst) success = 0 return "%s %s\n" % (success, r) permissions['known'] = 'pull' def known(repo, proto, nodes, others): return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes))) permissions['pushkey'] = 'push' def pushkey(repo, proto, namespace, key, old, new): # compatibility with pre-1.8 clients which were accidentally # sending raw binary nodes rather than utf-8-encoded hex if len(new) == 20 and new.encode('string-escape') != new: # looks like it could be a binary node try: new.decode('utf-8') new = encoding.tolocal(new) # but cleanly decodes as UTF-8 except UnicodeDecodeError: pass # binary, leave unmodified else: new = encoding.tolocal(new) # normal path if util.safehasattr(proto, 'restore'): proto.redirect() try: r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key), encoding.tolocal(old), new) or False except util.Abort: r = False output = proto.restore() return '%s\n%s' % (int(r), output) r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key), encoding.tolocal(old), new) return '%s\n' % int(r) def _allowstream(ui): return ui.configbool('server', 'uncompressed', True, untrusted=True) permissions['stream_out'] = 'pull' def stream(repo, proto): '''If the server supports streaming clone, it advertises the "stream" capability with a value representing the version and flags of the repo it is serving. Client checks to see if it understands the format. The format is simple: the server writes out a line with the amount of files, then the total amount of bytes to be transferred (separated by a space). Then, for each file, the server first writes the filename and filesize (separated by the null character), then the file contents. ''' if not _allowstream(repo.ui): return '1\n' entries = [] total_bytes = 0 try: # get consistent snapshot of repo, lock during scan lock = repo.lock() try: repo.ui.debug('scanning\n') for name, ename, size in repo.store.walk(): if size: entries.append((name, size)) total_bytes += size finally: lock.release() except error.LockError: return '2\n' # error: 2 def streamer(repo, entries, total): '''stream out all metadata files in repository.''' yield '0\n' # success repo.ui.debug('%d files, %d bytes to transfer\n' % (len(entries), total_bytes)) yield '%d %d\n' % (len(entries), total_bytes) sopener = repo.sopener oldaudit = sopener.mustaudit debugflag = repo.ui.debugflag sopener.mustaudit = False try: for name, size in entries: if debugflag: repo.ui.debug('sending %s (%d bytes)\n' % (name, size)) # partially encode name over the wire for backwards compat yield '%s\0%d\n' % (store.encodedir(name), size) if size <= 65536: fp = sopener(name) try: data = fp.read(size) finally: fp.close() yield data else: for chunk in util.filechunkiter(sopener(name), limit=size): yield chunk # replace with "finally:" when support for python 2.4 has been dropped except Exception: sopener.mustaudit = oldaudit raise sopener.mustaudit = oldaudit return streamres(streamer(repo, entries, total_bytes)) permissions['unbundle'] = 'push' def unbundle(repo, proto, heads): their_heads = decodelist(heads) def check_heads(): heads = repo.heads() heads_hash = util.sha1(''.join(sorted(heads))).digest() return (their_heads == ['force'] or their_heads == heads or their_heads == ['hashed', heads_hash]) proto.redirect() # fail early if possible if not check_heads(): return pusherr('repository changed while preparing changes - ' 'please try again') # write bundle data to temporary file because it can be big fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-') fp = os.fdopen(fd, 'wb+') r = 0 try: proto.getfile(fp) lock = repo.lock() try: if not check_heads(): # someone else committed/pushed/unbundled while we # were transferring data return pusherr('repository changed while uploading changes - ' 'please try again') # push can proceed fp.seek(0) gen = changegroupmod.readbundle(fp, None) try: r = repo.addchangegroup(gen, 'serve', proto._client()) except util.Abort, inst: sys.stderr.write("abort: %s\n" % inst) finally: lock.release() return pushres(r) finally: fp.close() os.unlink(tempname) commands = { 'batch': (batch, 'cmds *'), 'between': (between, 'pairs'), 'branchmap': (branchmap, ''), 'branches': (branches, 'nodes'), 'capabilities': (capabilities, ''), 'changegroup': (changegroup, 'roots'), 'changegroupsubset': (changegroupsubset, 'bases heads'), 'debugwireargs': (debugwireargs, 'one two *'), 'getbundle': (getbundle, '*'), 'heads': (heads, ''), 'hello': (hello, ''), 'known': (known, 'nodes *'), 'listkeys': (listkeys, 'namespace'), 'lookup': (lookup, 'key'), 'pushkey': (pushkey, 'namespace key old new'), 'stream_out': (stream, ''), 'unbundle': (unbundle, 'heads'), }