I am working on generating (actually editing) a mobileconfig file (aka iOS profile, XML) via bash script.
The script fetch data from a MS Database and has now to inject/replace this data in my mobileconfig file (XML).
The XML file has the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>Host</key>
<string>outlook.office365.com</string>
<key>MailNumberOfPastDaysToSync</key>
<integer>7</integer>
<key>Password</key>
<string>ActiveSyncPassword</string>
<key>PayloadDescription</key>
<string>Configures an Exchange account</string>
<key>PayloadDisplayName</key>
<string>Exchange ActiveSync</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>SSL</key>
<true/>
<key>UserName</key>
<string>[email protected]</string>
<key>disableMailRecentsSyncing</key>
<false/>
</dict>
<dict>
<key>AutoJoin</key>
<true/>
<key>EncryptionType</key>
<string>WPA</string>
<key>HIDDEN_NETWORK</key>
<true/>
<key>IsHotspot</key>
<false/>
<key>Password</key>
<string>WEPWPAWPSPEAPTLS</string>
<key>PayloadType</key>
<string>com.apple.wifi.managed</string>
<key>PayloadVersion</key>
<real>1</real>
<key>ProxyType</key>
<string>None</string>
<key>SSID_STR</key>
<string>SSID</string>
</dict>
<dict>
I would like to replace the WiFi Password but also ActiveSync "Password" fields between the < string> < /string> using any native (xmllint, sed) or non-native tool.
Can anyone please help ?
Editing structured data (such as XML) with plain-text tools invariably ends in misery when the file format changes in ways that nobody expects to make a difference (such as inserting benign whitespace). Instead, use a tool that parses XML properly and works on the tree, such as
xmlstarlet
.The general form for this is
Where
xpath
is an XPath expression that identifies the node you want to update, andvalue
is the new value you want to give it. The magic is in constructing an XPath expression that uniquely and reliably identifies the node you want to update. The MobileConfig XML format makes this somewhat harder than usual; after discussion in the comments we ended up withThe core of this is the XPath expression
..which requires some explanation. We use the following features:
//dict
matches anydict
node in the document,//dict/key
matches anykey
node that is the child of adict
node,//dict/key[text() = "Password"]
matches anykey
note that is the child of adict
node and contains the textPassword
,//dict/key[text() = "Password"]/following-sibling
matches any following sibling node of such akey
node, which is to say any node that is a child of the same parent and comes after thekey
node in the XML,//dict/key[text() = "Password"]/following-sibling::string
matches anystring
node that is such a following sibling node, and//dict/key[text() = "Password"]/following-sibling::string[1]
matches any node that is the first following siblingstring
node of such akey
node.We've already used a condition in
//dict/key[text() = "Password"]
; in order to find thedict
node whose password entry is to be changed, we need more of that. Thedict
node we want to find is identified byThat is a
dict
node that fulfills the conditionThe XPath expressions in this condition are all relative to the
dict
node that's being tested, sorefers to a
key
subnode of thatdict
node that contains the textPayloadDisplayName
, andis true if the text in the
string
node that follows thekey
node that contains the textPayloadDisplayName
isExchange ActiveSync
. So we chuck that into the simplified expression I explained above and get the full filter.I feel compelled to point out that the structure of this XML file makes the whole thing more difficult than necessary or usual. Sanely structured XML can be handled with much simpler XPath expressions (most of the time).