Skip to content

Commit 5d85e53

Browse files
author
Steve Ramage
committed
feat: validation for IP addresses in Unit and Network files (Resolves #361)
1 parent 9f85745 commit 5d85e53

25 files changed

+852
-27
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.intentions
2+
3+
import com.intellij.codeInspection.LocalQuickFix
4+
import com.intellij.codeInspection.ProblemDescriptor
5+
import com.intellij.openapi.fileEditor.FileEditorManager
6+
import com.intellij.openapi.project.Project
7+
import com.intellij.pom.Navigatable
8+
import com.intellij.psi.util.PsiTreeUtil
9+
import com.intellij.psi.util.childrenOfType
10+
import com.intellij.psi.util.endOffset
11+
import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups
12+
13+
class AddPropertyAndValueQuickFix(val section: String, val key: String, val value: String) : LocalQuickFix {
14+
override fun getFamilyName(): String {
15+
return "Add ${key}=${value} to ${section}"
16+
}
17+
18+
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
19+
20+
descriptor.psiElement.containingFile ?: return
21+
val sectionGroup = PsiTreeUtil.getParentOfType(descriptor.psiElement, UnitFileSectionGroups::class.java) ?: return
22+
val dummyFile = UnitElementFactory.createFile(project, sectionGroup.text + "\n${key}=${value}")
23+
24+
val newElement = sectionGroup.replace(dummyFile.firstChild)
25+
26+
(dummyFile.lastChild.navigationElement as? Navigatable)?.navigate(true)
27+
28+
FileEditorManager.getInstance(project).selectedTextEditor?.caretModel?.moveToOffset(newElement.endOffset)
29+
}
30+
31+
}
32+

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ class SemanticDataRepository private constructor() {
152152
validatorMap.putAll(CPUSharesOptionValue.validators)
153153
validatorMap.putAll(CgroupSocketBindOptionValue.validators)
154154
validatorMap.putAll(RlimitOptionValue.validators) // Scopes are not supported since they aren't standard unit files.
155+
validatorMap.putAll(NetworkAddressOptionValue.validators)
156+
validatorMap.putAll(InAddrPrefixesOptionValue.validators)
155157
fileClassToSectionNameToKeyValuesFromDoc["unit"]?.remove(SCOPE_KEYWORD)
156158
fileClassToSectionToKeyAndValidatorMap["unit"]?.remove(SCOPE_KEYWORD)
157159
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*
5+
6+
class InAddrPrefixesOptionValue(combinator: Combinator) : GrammarOptionValue("config_parse_in_addr_prefixes", combinator) {
7+
8+
companion object {
9+
10+
val validators = mapOf(
11+
Validator("config_parse_in_addr_prefixes", "AF_UNSPEC") to InAddrPrefixesOptionValue(SequenceCombinator(IP_ADDR_PREFIX_LIST, EOF())),
12+
Validator("config_parse_in_addr_prefixes", "AF_INET") to InAddrPrefixesOptionValue(SequenceCombinator(IPV4_ADDR_PREFIX_LIST, EOF())),
13+
Validator("config_parse_in_addr_prefixes", "AF_INET6") to InAddrPrefixesOptionValue(SequenceCombinator(IPV6_ADDR_PREFIX_LIST, EOF()))
14+
)
15+
}
16+
}
17+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.EOF
5+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.GrammarOptionValue
6+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.IP_ADDR_AND_PREFIX_LENGTH
7+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.SequenceCombinator
8+
9+
class NetworkAddressOptionValue() : GrammarOptionValue("config_parse_address_section", GRAMMAR) {
10+
11+
companion object {
12+
val GRAMMAR = SequenceCombinator(
13+
IP_ADDR_AND_PREFIX_LENGTH, EOF())
14+
15+
val validators = mapOf(
16+
Validator("config_parse_address_section", "ADDRESS_ADDRESS") to NetworkAddressOptionValue()
17+
)
18+
}
19+
}
20+

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/AlternativeCombinator.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,21 @@ open class AlternativeCombinator(vararg val tokens: Combinator) : Combinator {
3737
override fun SemanticMatch(value: String, offset: Int): MatchResult {
3838
return match(value, offset, Combinator::SemanticMatch)
3939
}
40+
41+
override fun toString(): String = toStringIndented(0)
42+
43+
override fun toStringIndented(indent: Int): String {
44+
val prefix = " ".repeat(indent)
45+
val sb = StringBuilder()
46+
sb.append(prefix).append("Alt(\n")
47+
for (token in tokens) {
48+
if (token is SequenceCombinator || token is AlternativeCombinator || token is Repeat || token is ZeroOrOne || token is ZeroOrMore || token is OneOrMore) {
49+
sb.append(token.toStringIndented(indent + 1)).append("\n")
50+
} else {
51+
sb.append(" ".repeat(indent + 1)).append(token.toString()).append("\n")
52+
}
53+
}
54+
sb.append(prefix).append(")")
55+
return sb.toString()
56+
}
4057
}

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinator.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ interface Combinator {
3232
*/
3333
fun SemanticMatch(value : String, offset: Int): MatchResult
3434

35+
fun toStringIndented(indent: Int): String
3536
}

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/Combinators.kt

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,94 @@ package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.gra
33
val BYTES = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*")
44
val DEVICE = RegexTerminal("\\S+\\s*", "/[^\\u0000. ]+\\s*")
55
val IOPS = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*")
6+
7+
var IPV4_OCTET = IntegerTerminal(0, 256)
8+
val DOT = LiteralChoiceTerminal(".")
9+
var IPV4_ADDR = SequenceCombinator(IPV4_OCTET, DOT, IPV4_OCTET, DOT, IPV4_OCTET, DOT, IPV4_OCTET)
10+
11+
val CIDR_SEPARATOR = LiteralChoiceTerminal("/")
12+
13+
val IPV4_ADDR_AND_PREFIX_LENGTH = SequenceCombinator(IPV4_ADDR, CIDR_SEPARATOR, IntegerTerminal(8, 33))
14+
val IPV4_ADDR_AND_OPTIONAL_PREFIX_LENGTH = SequenceCombinator(IPV4_ADDR, ZeroOrOne(SequenceCombinator( CIDR_SEPARATOR, IntegerTerminal(8, 33))))
15+
16+
var IPV6_HEXTET = RegexTerminal("[0-9a-fA-F]{1,4}", "[0-9a-fA-F]{1,4}")
17+
val COLON = LiteralChoiceTerminal(":")
18+
val DOUBLE_COLON = LiteralChoiceTerminal("::")
19+
20+
21+
val IPV6_FULL_SPECIFIED = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET)
22+
val IPV6_ZERO_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(DOUBLE_COLON, ZeroOrOne(SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 8), IPV6_HEXTET)))
23+
val IPV6_ONE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(SequenceCombinator( Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 7), IPV6_HEXTET)))
24+
val IPV6_TWO_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 6), IPV6_HEXTET)))
25+
val IPV6_THREE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 5), IPV6_HEXTET)))
26+
val IPV6_FOUR_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 4), IPV6_HEXTET)))
27+
val IPV6_FIVE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 3), IPV6_HEXTET)))
28+
val IPV6_SIX_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 2), IPV6_HEXTET)))
29+
val IPV6_SEVEN_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, COLON,IPV6_HEXTET, DOUBLE_COLON, ZeroOrOne(IPV6_HEXTET))
30+
31+
32+
val IPV6_IPV4_SUFFIX_FULL = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV4_ADDR)
33+
val IPV6_IPV4_SUFFIX_ZERO_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(DOUBLE_COLON,SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 6), IPV4_ADDR))
34+
val IPV6_IPV4_SUFFIX_ONE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, DOUBLE_COLON, SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 5), IPV4_ADDR))
35+
val IPV6_IPV4_SUFFIX_TWO_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, DOUBLE_COLON, SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 4), IPV4_ADDR))
36+
val IPV6_IPV4_SUFFIX_THREE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, DOUBLE_COLON,SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 3), IPV4_ADDR))
37+
val IPV6_IPV4_SUFFIX_FOUR_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, DOUBLE_COLON, SequenceCombinator(Repeat(SequenceCombinator(IPV6_HEXTET, COLON), 0, 2), IPV4_ADDR))
38+
val IPV6_IPV4_SUFFIX_FIVE_HEXTET_BEFORE_ZERO_COMP = SequenceCombinator(IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, COLON, IPV6_HEXTET, DOUBLE_COLON, IPV4_ADDR)
39+
40+
//val IPV6_ALL_ZEROS = DOUBLE_COLON
41+
42+
val IPV6_ADDR = AlternativeCombinator(
43+
IPV6_IPV4_SUFFIX_FULL,
44+
IPV6_IPV4_SUFFIX_ZERO_HEXTET_BEFORE_ZERO_COMP,
45+
IPV6_IPV4_SUFFIX_ONE_HEXTET_BEFORE_ZERO_COMP,
46+
IPV6_IPV4_SUFFIX_TWO_HEXTET_BEFORE_ZERO_COMP,
47+
IPV6_IPV4_SUFFIX_THREE_HEXTET_BEFORE_ZERO_COMP,
48+
IPV6_IPV4_SUFFIX_FOUR_HEXTET_BEFORE_ZERO_COMP,
49+
IPV6_IPV4_SUFFIX_FIVE_HEXTET_BEFORE_ZERO_COMP,
50+
IPV6_FULL_SPECIFIED,
51+
IPV6_SEVEN_HEXTET_BEFORE_ZERO_COMP,
52+
IPV6_SIX_HEXTET_BEFORE_ZERO_COMP,
53+
IPV6_FIVE_HEXTET_BEFORE_ZERO_COMP,
54+
IPV6_FOUR_HEXTET_BEFORE_ZERO_COMP,
55+
IPV6_THREE_HEXTET_BEFORE_ZERO_COMP,
56+
IPV6_TWO_HEXTET_BEFORE_ZERO_COMP,
57+
IPV6_ONE_HEXTET_BEFORE_ZERO_COMP,
58+
// Must go last because it's the most general and can match ::
59+
IPV6_ZERO_HEXTET_BEFORE_ZERO_COMP,
60+
61+
// I suspect maybe that this one is redundant
62+
//IPV6_ALL_ZEROS,
63+
)
64+
65+
val IPV6_ADDR_AND_PREFIX_LENGTH = SequenceCombinator(IPV6_ADDR, CIDR_SEPARATOR, IntegerTerminal(64, 129))
66+
val IPV6_ADDR_AND_OPTIONAL_PREFIX_LENGTH = SequenceCombinator(IPV6_ADDR, ZeroOrOne(SequenceCombinator(CIDR_SEPARATOR, IntegerTerminal(64, 129))))
67+
68+
69+
var IP_ADDR_AND_PREFIX_LENGTH = AlternativeCombinator(
70+
IPV4_ADDR_AND_OPTIONAL_PREFIX_LENGTH,
71+
IPV6_ADDR_AND_PREFIX_LENGTH)
72+
73+
var IN_ADDR_PREFIX_SPECIAL_VALUES = LiteralChoiceTerminal("any", "localhost", "link-local", "multicast")
74+
75+
var IPV4_ADDR_AND_PREFIX_OR_SPECIAL = AlternativeCombinator(
76+
IPV4_ADDR_AND_OPTIONAL_PREFIX_LENGTH,
77+
IN_ADDR_PREFIX_SPECIAL_VALUES,
78+
)
79+
80+
var IPV6_ADDR_AND_PREFIX_OR_SPECIAL = AlternativeCombinator(
81+
IPV6_ADDR_AND_OPTIONAL_PREFIX_LENGTH,
82+
IN_ADDR_PREFIX_SPECIAL_VALUES,
83+
)
84+
85+
86+
var IP_ADDR_AND_PREFIX_OR_SPECIAL = AlternativeCombinator(
87+
IPV4_ADDR_AND_OPTIONAL_PREFIX_LENGTH,
88+
IPV6_ADDR_AND_OPTIONAL_PREFIX_LENGTH,
89+
IN_ADDR_PREFIX_SPECIAL_VALUES,
90+
)
91+
92+
var IP_ADDR_PREFIX_LIST = SequenceCombinator(IP_ADDR_AND_PREFIX_OR_SPECIAL, ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), IP_ADDR_AND_PREFIX_OR_SPECIAL)))
93+
var IPV4_ADDR_PREFIX_LIST = SequenceCombinator(IPV4_ADDR_AND_PREFIX_OR_SPECIAL, ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), IPV4_ADDR_AND_PREFIX_OR_SPECIAL)))
94+
var IPV6_ADDR_PREFIX_LIST = SequenceCombinator(IPV6_ADDR_AND_PREFIX_OR_SPECIAL, ZeroOrMore(SequenceCombinator(WhitespaceTerminal(), IPV6_ADDR_AND_PREFIX_OR_SPECIAL)))
95+
96+

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/EOF.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@ class EOF : Combinator {
1616
NoMatch
1717
}
1818
}
19+
20+
override fun toStringIndented(indent: Int): String {
21+
return "EOF"
22+
}
1923
}

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/FlexibleLiteralChoiceTerminal.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,15 @@ class FlexibleLiteralChoiceTerminal(vararg val choices: String) : TerminalCombin
9191
return NoMatch.copy(longestMatch = offset)
9292
}
9393

94+
override fun toString(): String {
95+
return if (choices.size == 1) {
96+
"Literal(\"${choices[0]}\")"
97+
} else {
98+
"FlexLitChoice(" + choices.joinToString(",") { "\"$it\"" } + ")"
99+
}
100+
}
94101

102+
override fun toStringIndented(indent: Int): String {
103+
return toString()
104+
}
95105
}

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/grammar/IntegerTerminal.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ class IntegerTerminal(private val minInclusive: Int,private val maxExclusive: In
2222

2323
return MatchResult(listOf(matchResult.value), offset + matchResult.value.length, listOf(this), offset + matchResult.value.length)
2424
}
25+
26+
override fun toString(): String {
27+
return "Int($minInclusive,$maxExclusive)"
28+
}
2529
}

0 commit comments

Comments
 (0)