Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Paste
P43
paste.php
Active
Public
Actions
Authored by
revi
on Jun 19 2024, 14:04.
Edit Paste
Archive Paste
View Raw File
Subscribe
Mute Notifications
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Tags
None
Referenced Files
F2232595: paste.php
Jun 19 2024, 14:04
2024-06-19 14:04:04 (UTC+0)
Subscribers
None
<?php
final
class
PhabricatorEditorURIEngine
extends
Phobject
{
private
$viewer
;
private
$repository
;
private
$pattern
;
private
$rawTokens
;
private
$repositoryTokens
;
public
static
function
newForViewer
(
PhabricatorUser
$viewer
)
{
if
(
!
$viewer
->
isLoggedIn
(
)
)
{
return
null
;
}
$pattern
=
$viewer
->
getUserSetting
(
PhabricatorEditorSetting
::
SETTINGKEY
)
;
if
(
$pattern
===
null
||
trim
(
$pattern
)
===
''
)
{
return
null
;
}
$engine
=
id
(
new
self
(
)
)
->
setViewer
(
$viewer
)
->
setPattern
(
$pattern
)
;
// If there's a problem with the pattern,
try
{
$engine
->
validatePattern
(
)
;
}
catch
(
PhabricatorEditorURIParserException
$ex
)
{
return
null
;
}
return
$engine
;
}
public
function
setViewer
(
PhabricatorUser
$viewer
)
{
$this
->
viewer
=
$viewer
;
return
$this
;
}
public
function
getViewer
(
)
{
return
$this
->
viewer
;
}
public
function
setRepository
(
PhabricatorRepository
$repository
)
{
$this
->
repository
=
$repository
;
return
$this
;
}
public
function
getRepository
(
)
{
return
$this
->
repository
;
}
public
function
setPattern
(
$pattern
)
{
$this
->
pattern
=
$pattern
;
return
$this
;
}
public
function
getPattern
(
)
{
return
$this
->
pattern
;
}
public
function
validatePattern
(
)
{
$this
->
getRawURITokens
(
)
;
return
true
;
}
public
function
getURIForPath
(
$path
,
$line
)
{
$tokens
=
$this
->
getURITokensForRepository
(
$path
)
;
$variables
=
array
(
'f'
=>
$this
->
escapeToken
(
$path
)
,
'l'
=>
$this
->
escapeToken
(
$line
)
,
)
;
$tokens
=
$this
->
newTokensWithVariables
(
$tokens
,
$variables
)
;
return
$this
->
newStringFromTokens
(
$tokens
)
;
}
public
function
getURITokensForPath
(
$path
)
{
$tokens
=
$this
->
getURITokensForRepository
(
$path
)
;
$variables
=
array
(
'f'
=>
$this
->
escapeToken
(
$path
)
,
)
;
return
$this
->
newTokensWithVariables
(
$tokens
,
$variables
)
;
}
public
static
function
getVariableDefinitions
(
)
{
return
array
(
'f'
=>
array
(
'name'
=>
pht
(
'File Name'
)
,
'example'
=>
pht
(
'path/to/source.c'
)
,
)
,
'l'
=>
array
(
'name'
=>
pht
(
'Line Number'
)
,
'example'
=>
'777'
,
)
,
'n'
=>
array
(
'name'
=>
pht
(
'Repository Short Name'
)
,
'example'
=>
'arcanist'
,
)
,
'd'
=>
array
(
'name'
=>
pht
(
'Repository ID'
)
,
'example'
=>
'42'
,
)
,
'p'
=>
array
(
'name'
=>
pht
(
'Repository PHID'
)
,
'example'
=>
'PHID-REPO-abcdefghijklmnopqrst'
,
)
,
'r'
=>
array
(
'name'
=>
pht
(
'Repository Callsign'
)
,
'example'
=>
'XYZ'
,
)
,
'%'
=>
array
(
'name'
=>
pht
(
'Literal Percent Symbol'
)
,
'example'
=>
'%'
,
)
,
)
;
}
private
function
getURITokensForRepository
(
)
{
if
(
!
$this
->
repositoryTokens
)
{
$this
->
repositoryTokens
=
$this
->
newURITokensForRepository
(
)
;
}
return
$this
->
repositoryTokens
;
}
private
function
newURITokensForRepository
(
)
{
$tokens
=
$this
->
getRawURITokens
(
)
;
$repository
=
$this
->
getRepository
(
)
;
if
(
!
$repository
)
{
throw
new
PhutilInvalidStateException
(
'setRepository'
)
;
}
$variables
=
array
(
'r'
=>
$this
->
escapeToken
(
$repository
->
getCallsign
(
)
)
,
'n'
=>
$this
->
escapeToken
(
$repository
->
getRepositorySlug
(
)
)
,
'd'
=>
$this
->
escapeToken
(
$repository
->
getID
(
)
)
,
'p'
=>
$this
->
escapeToken
(
$repository
->
getPHID
(
)
)
,
)
;
return
$this
->
newTokensWithVariables
(
$tokens
,
$variables
)
;
}
private
function
getRawURITokens
(
)
{
if
(
!
$this
->
rawTokens
)
{
$this
->
rawTokens
=
$this
->
newRawURITokens
(
)
;
}
return
$this
->
rawTokens
;
}
private
function
newRawURITokens
(
)
{
$raw_pattern
=
$this
->
getPattern
(
)
;
$raw_tokens
=
self
::
newPatternTokens
(
$raw_pattern
)
;
$variable_definitions
=
self
::
getVariableDefinitions
(
)
;
foreach
(
$raw_tokens
as
$token
)
{
if
(
$token
[
'type'
]
!==
'variable'
)
{
continue
;
}
$value
=
$token
[
'value'
]
;
if
(
isset
(
$variable_definitions
[
$value
]
)
)
{
continue
;
}
throw
new
PhabricatorEditorURIParserException
(
pht
(
'Editor pattern "%s" is invalid: the pattern contains an '
.
'unrecognized variable ("%s"). Use "%%%%" to encode a literal '
.
'percent symbol.'
,
$raw_pattern
,
'%'
.
$value
)
)
;
}
$variables
=
array
(
'%'
=>
'%'
,
)
;
$tokens
=
$this
->
newTokensWithVariables
(
$raw_tokens
,
$variables
)
;
$first_literal
=
null
;
if
(
$tokens
)
{
foreach
(
$tokens
as
$token
)
{
if
(
$token
[
'type'
]
===
'literal'
)
{
$first_literal
=
$token
[
'value'
]
;
}
break
;
}
if
(
$first_literal
===
null
)
{
throw
new
PhabricatorEditorURIParserException
(
pht
(
'Editor pattern "%s" is invalid: the pattern must begin with '
.
'a valid editor protocol, but begins with a variable. This is '
.
'very sneaky and also very forbidden.'
,
$raw_pattern
)
)
;
}
}
$uri
=
new
PhutilURI
(
$first_literal
)
;
$editor_protocol
=
$uri
->
getProtocol
(
)
;
if
(
!
$editor_protocol
)
{
throw
new
PhabricatorEditorURIParserException
(
pht
(
'Editor pattern "%s" is invalid: the pattern must begin with '
.
'a valid editor protocol, but does not begin with a recognized '
.
'protocol string.'
,
$raw_pattern
)
)
;
}
$allowed_key
=
'uri.allowed-editor-protocols'
;
$allowed_protocols
=
PhabricatorEnv
::
getEnvConfig
(
$allowed_key
)
;
if
(
empty
(
$allowed_protocols
[
$editor_protocol
]
)
)
{
throw
new
PhabricatorEditorURIParserException
(
pht
(
'Editor pattern "%s" is invalid: the pattern must begin with '
.
'a valid editor protocol, but the protocol "%s://" is not allowed.'
,
$raw_pattern
,
$editor_protocol
)
)
;
}
return
$tokens
;
}
private
function
newTokensWithVariables
(
array
$tokens
,
array
$variables
)
{
// Replace all "variable" tokens that we have replacements for with
// the literal value.
foreach
(
$tokens
as
$key
=>
$token
)
{
$type
=
$token
[
'type'
]
;
if
(
$type
==
'variable'
)
{
$variable
=
$token
[
'value'
]
;
if
(
isset
(
$variables
[
$variable
]
)
)
{
$tokens
[
$key
]
=
array
(
'type'
=>
'literal'
,
'value'
=>
$variables
[
$variable
]
,
)
;
}
}
}
// Now, merge sequences of adjacent "literal" tokens into a single token.
$last_literal
=
null
;
foreach
(
$tokens
as
$key
=>
$token
)
{
$is_literal
=
(
$token
[
'type'
]
===
'literal'
)
;
if
(
!
$is_literal
)
{
$last_literal
=
null
;
continue
;
}
if
(
$last_literal
!==
null
)
{
$tokens
[
$key
]
[
'value'
]
=
$tokens
[
$last_literal
]
[
'value'
]
.
$token
[
'value'
]
;
unset
(
$tokens
[
$last_literal
]
)
;
}
$last_literal
=
$key
;
}
$tokens
=
array_values
(
$tokens
)
;
return
$tokens
;
}
private
function
escapeToken
(
$token
)
{
// Paths are user controlled, so a clever user could potentially make
// editor links do surprising things with paths containing "/../".
// Find anything that looks like "/../" and mangle it.
$token
=
preg_replace
(
'((^|/)\.\.(/|\z))'
,
'\1dot-dot\2'
,
$token
)
;
return
phutil_escape_uri
(
$token
)
;
}
private
function
newStringFromTokens
(
array
$tokens
)
{
$result
=
array
(
)
;
foreach
(
$tokens
as
$token
)
{
$token_type
=
$token
[
'type'
]
;
$token_value
=
$token
[
'value'
]
;
$is_literal
=
(
$token_type
===
'literal'
)
;
if
(
!
$is_literal
)
{
throw
new
Exception
(
pht
(
'Editor pattern token list can not be converted into a string: '
.
'it still contains a non-literal token ("%s", of type "%s").'
,
$token_value
,
$token_type
)
)
;
}
$result
[
]
=
$token_value
;
}
$result
=
implode
(
''
,
$result
)
;
return
$result
;
}
public
static
function
newPatternTokens
(
$raw_pattern
)
{
$token_positions
=
array
(
)
;
$len
=
strlen
(
$raw_pattern
)
;
for
(
$ii
=
0
;
$ii
<
$len
;
$ii
++
)
{
$c
=
$raw_pattern
[
$ii
]
;
if
(
$c
===
'%'
)
{
if
(
!
isset
(
$raw_pattern
[
$ii
+
1
]
)
)
{
throw
new
PhabricatorEditorURIParserException
(
pht
(
'Editor pattern "%s" is invalid: the final character in a '
.
'pattern may not be an unencoded percent symbol ("%%"). '
.
'Use "%%%%" to encode a literal percent symbol.'
,
$raw_pattern
)
)
;
}
$token_positions
[
]
=
$ii
;
$ii
++
;
}
}
// Add a final marker past the end of the string, so we'll collect any
// trailing literal bytes.
$token_positions
[
]
=
$len
;
$tokens
=
array
(
)
;
$cursor
=
0
;
foreach
(
$token_positions
as
$pos
)
{
$token_len
=
(
$pos
-
$cursor
)
;
if
(
$token_len
>
0
)
{
$tokens
[
]
=
array
(
'type'
=>
'literal'
,
'value'
=>
substr
(
$raw_pattern
,
$cursor
,
$token_len
)
,
)
;
}
$cursor
=
$pos
;
if
(
$cursor
<
$len
)
{
$tokens
[
]
=
array
(
'type'
=>
'variable'
,
'value'
=>
substr
(
$raw_pattern
,
$cursor
+
1
,
1
)
,
)
;
}
$cursor
=
$pos
+
2
;
}
return
$tokens
;
}
}
Event Timeline
revi
created this paste.
Jun 19 2024, 14:04
2024-06-19 14:04:04 (UTC+0)
revi
created this object in space
S1 Public
.
revi
mentioned this in
T15824: Deprecation notice at PhabricatorEditorURIEngine.php:283
.
Log In to Comment