Home | History | Annotate | Download | only in adapter
      1 /* Copyright (C) 2010 The Android Open Source Project.
      2  *
      3  * Licensed under the Apache License, Version 2.0 (the "License");
      4  * you may not use this file except in compliance with the License.
      5  * You may obtain a copy of the License at
      6  *
      7  *      http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software
     10  * distributed under the License is distributed on an "AS IS" BASIS,
     11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12  * See the License for the specific language governing permissions and
     13  * limitations under the License.
     14  */
     15 
     16 package com.android.exchange.adapter;
     17 
     18 import com.android.email.SecurityPolicy;
     19 import com.android.email.SecurityPolicy.PolicySet;
     20 import com.android.exchange.EasSyncService;
     21 
     22 import org.xmlpull.v1.XmlPullParser;
     23 import org.xmlpull.v1.XmlPullParserException;
     24 import org.xmlpull.v1.XmlPullParserFactory;
     25 
     26 import java.io.ByteArrayInputStream;
     27 import java.io.IOException;
     28 import java.io.InputStream;
     29 
     30 /**
     31  * Parse the result of the Provision command
     32  *
     33  * Assuming a successful parse, we store the PolicySet and the policy key
     34  */
     35 public class ProvisionParser extends Parser {
     36     private EasSyncService mService;
     37     PolicySet mPolicySet = null;
     38     String mPolicyKey = null;
     39     boolean mRemoteWipe = false;
     40     boolean mIsSupportable = true;
     41 
     42     public ProvisionParser(InputStream in, EasSyncService service) throws IOException {
     43         super(in);
     44         mService = service;
     45     }
     46 
     47     public PolicySet getPolicySet() {
     48         return mPolicySet;
     49     }
     50 
     51     public String getPolicyKey() {
     52         return mPolicyKey;
     53     }
     54 
     55     public boolean getRemoteWipe() {
     56         return mRemoteWipe;
     57     }
     58 
     59     public boolean hasSupportablePolicySet() {
     60         return (mPolicySet != null) && mIsSupportable;
     61     }
     62 
     63     private void parseProvisionDocWbxml() throws IOException {
     64         int minPasswordLength = 0;
     65         int passwordMode = PolicySet.PASSWORD_MODE_NONE;
     66         int maxPasswordFails = 0;
     67         int maxScreenLockTime = 0;
     68         boolean supported = true;
     69 
     70         while (nextTag(Tags.PROVISION_EAS_PROVISION_DOC) != END) {
     71             switch (tag) {
     72                 case Tags.PROVISION_DEVICE_PASSWORD_ENABLED:
     73                     if (getValueInt() == 1) {
     74                         if (passwordMode == PolicySet.PASSWORD_MODE_NONE) {
     75                             passwordMode = PolicySet.PASSWORD_MODE_SIMPLE;
     76                         }
     77                     }
     78                     break;
     79                 case Tags.PROVISION_MIN_DEVICE_PASSWORD_LENGTH:
     80                     minPasswordLength = getValueInt();
     81                     break;
     82                 case Tags.PROVISION_ALPHA_DEVICE_PASSWORD_ENABLED:
     83                     if (getValueInt() == 1) {
     84                         passwordMode = PolicySet.PASSWORD_MODE_STRONG;
     85                     }
     86                     break;
     87                 case Tags.PROVISION_MAX_INACTIVITY_TIME_DEVICE_LOCK:
     88                     // EAS gives us seconds, which is, happily, what the PolicySet requires
     89                     maxScreenLockTime = getValueInt();
     90                     break;
     91                 case Tags.PROVISION_MAX_DEVICE_PASSWORD_FAILED_ATTEMPTS:
     92                     maxPasswordFails = getValueInt();
     93                     break;
     94                 case Tags.PROVISION_ALLOW_SIMPLE_DEVICE_PASSWORD:
     95                     // Ignore this unless there's any MSFT documentation for what this means
     96                     // Hint: I haven't seen any that's more specific than "simple"
     97                     getValue();
     98                     break;
     99                 // The following policy, if false, can't be supported at the moment
    100                 case Tags.PROVISION_ATTACHMENTS_ENABLED:
    101                     if (getValueInt() == 0) {
    102                        supported = false;
    103                     }
    104                     break;
    105                 // The following policies, if true, can't be supported at the moment
    106                 case Tags.PROVISION_DEVICE_ENCRYPTION_ENABLED:
    107                 case Tags.PROVISION_PASSWORD_RECOVERY_ENABLED:
    108                 case Tags.PROVISION_DEVICE_PASSWORD_EXPIRATION:
    109                 case Tags.PROVISION_DEVICE_PASSWORD_HISTORY:
    110                 case Tags.PROVISION_MAX_ATTACHMENT_SIZE:
    111                     if (getValueInt() == 1) {
    112                         supported = false;
    113                     }
    114                     break;
    115                 default:
    116                     skipTag();
    117             }
    118 
    119             if (!supported) {
    120                 log("Policy not supported: " + tag);
    121                 mIsSupportable = false;
    122             }
    123         }
    124 
    125         mPolicySet = new SecurityPolicy.PolicySet(minPasswordLength, passwordMode,
    126                     maxPasswordFails, maxScreenLockTime, true);
    127     }
    128 
    129     class ShadowPolicySet {
    130         int mMinPasswordLength = 0;
    131         int mPasswordMode = PolicySet.PASSWORD_MODE_NONE;
    132         int mMaxPasswordFails = 0;
    133         int mMaxScreenLockTime = 0;
    134     }
    135 
    136     public void parseProvisionDocXml(String doc) throws IOException {
    137         ShadowPolicySet sps = new ShadowPolicySet();
    138 
    139         try {
    140             XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    141             XmlPullParser parser = factory.newPullParser();
    142             parser.setInput(new ByteArrayInputStream(doc.getBytes()), "UTF-8");
    143             int type = parser.getEventType();
    144             if (type == XmlPullParser.START_DOCUMENT) {
    145                 type = parser.next();
    146                 if (type == XmlPullParser.START_TAG) {
    147                     String tagName = parser.getName();
    148                     if (tagName.equals("wap-provisioningdoc")) {
    149                         parseWapProvisioningDoc(parser, sps);
    150                     }
    151                 }
    152             }
    153         } catch (XmlPullParserException e) {
    154            throw new IOException();
    155         }
    156 
    157         mPolicySet = new PolicySet(sps.mMinPasswordLength, sps.mPasswordMode, sps.mMaxPasswordFails,
    158                 sps.mMaxScreenLockTime, true);
    159     }
    160 
    161     /**
    162      * Return true if password is required; otherwise false.
    163      */
    164     boolean parseSecurityPolicy(XmlPullParser parser, ShadowPolicySet sps)
    165             throws XmlPullParserException, IOException {
    166         boolean passwordRequired = true;
    167         while (true) {
    168             int type = parser.nextTag();
    169             if (type == XmlPullParser.END_TAG && parser.getName().equals("characteristic")) {
    170                 break;
    171             } else if (type == XmlPullParser.START_TAG) {
    172                 String tagName = parser.getName();
    173                 if (tagName.equals("parm")) {
    174                     String name = parser.getAttributeValue(null, "name");
    175                     if (name.equals("4131")) {
    176                         String value = parser.getAttributeValue(null, "value");
    177                         if (value.equals("1")) {
    178                             passwordRequired = false;
    179                         }
    180                     }
    181                 }
    182             }
    183         }
    184         return passwordRequired;
    185     }
    186 
    187     void parseCharacteristic(XmlPullParser parser, ShadowPolicySet sps)
    188             throws XmlPullParserException, IOException {
    189         boolean enforceInactivityTimer = true;
    190         while (true) {
    191             int type = parser.nextTag();
    192             if (type == XmlPullParser.END_TAG && parser.getName().equals("characteristic")) {
    193                 break;
    194             } else if (type == XmlPullParser.START_TAG) {
    195                 if (parser.getName().equals("parm")) {
    196                     String name = parser.getAttributeValue(null, "name");
    197                     String value = parser.getAttributeValue(null, "value");
    198                     if (name.equals("AEFrequencyValue")) {
    199                         if (enforceInactivityTimer) {
    200                             if (value.equals("0")) {
    201                                 sps.mMaxScreenLockTime = 1;
    202                             } else {
    203                                 sps.mMaxScreenLockTime = 60*Integer.parseInt(value);
    204                             }
    205                         }
    206                     } else if (name.equals("AEFrequencyType")) {
    207                         // "0" here means we don't enforce an inactivity timeout
    208                         if (value.equals("0")) {
    209                             enforceInactivityTimer = false;
    210                         }
    211                     } else if (name.equals("DeviceWipeThreshold")) {
    212                         sps.mMaxPasswordFails = Integer.parseInt(value);
    213                     } else if (name.equals("CodewordFrequency")) {
    214                         // Ignore; has no meaning for us
    215                     } else if (name.equals("MinimumPasswordLength")) {
    216                         sps.mMinPasswordLength = Integer.parseInt(value);
    217                     } else if (name.equals("PasswordComplexity")) {
    218                         if (value.equals("0")) {
    219                             sps.mPasswordMode = PolicySet.PASSWORD_MODE_STRONG;
    220                         } else {
    221                             sps.mPasswordMode = PolicySet.PASSWORD_MODE_SIMPLE;
    222                         }
    223                     }
    224                 }
    225             }
    226         }
    227     }
    228 
    229     void parseRegistry(XmlPullParser parser, ShadowPolicySet sps)
    230             throws XmlPullParserException, IOException {
    231       while (true) {
    232           int type = parser.nextTag();
    233           if (type == XmlPullParser.END_TAG && parser.getName().equals("characteristic")) {
    234               break;
    235           } else if (type == XmlPullParser.START_TAG) {
    236               String name = parser.getName();
    237               if (name.equals("characteristic")) {
    238                   parseCharacteristic(parser, sps);
    239               }
    240           }
    241       }
    242     }
    243 
    244     void parseWapProvisioningDoc(XmlPullParser parser, ShadowPolicySet sps)
    245             throws XmlPullParserException, IOException {
    246         while (true) {
    247             int type = parser.nextTag();
    248             if (type == XmlPullParser.END_TAG && parser.getName().equals("wap-provisioningdoc")) {
    249                 break;
    250             } else if (type == XmlPullParser.START_TAG) {
    251                 String name = parser.getName();
    252                 if (name.equals("characteristic")) {
    253                     String atype = parser.getAttributeValue(null, "type");
    254                     if (atype.equals("SecurityPolicy")) {
    255                         // If a password isn't required, stop here
    256                         if (!parseSecurityPolicy(parser, sps)) {
    257                             return;
    258                         }
    259                     } else if (atype.equals("Registry")) {
    260                         parseRegistry(parser, sps);
    261                         return;
    262                     }
    263                 }
    264             }
    265         }
    266     }
    267 
    268     public void parseProvisionData() throws IOException {
    269         while (nextTag(Tags.PROVISION_DATA) != END) {
    270             if (tag == Tags.PROVISION_EAS_PROVISION_DOC) {
    271                 parseProvisionDocWbxml();
    272             } else {
    273                 skipTag();
    274             }
    275         }
    276     }
    277 
    278     public void parsePolicy() throws IOException {
    279         String policyType = null;
    280         while (nextTag(Tags.PROVISION_POLICY) != END) {
    281             switch (tag) {
    282                 case Tags.PROVISION_POLICY_TYPE:
    283                     policyType = getValue();
    284                     mService.userLog("Policy type: ", policyType);
    285                     break;
    286                 case Tags.PROVISION_POLICY_KEY:
    287                     mPolicyKey = getValue();
    288                     break;
    289                 case Tags.PROVISION_STATUS:
    290                     mService.userLog("Policy status: ", getValue());
    291                     break;
    292                 case Tags.PROVISION_DATA:
    293                     if (policyType.equalsIgnoreCase(EasSyncService.EAS_2_POLICY_TYPE)) {
    294                         // Parse the old style XML document
    295                         parseProvisionDocXml(getValue());
    296                     } else {
    297                         // Parse the newer WBXML data
    298                         parseProvisionData();
    299                     }
    300                     break;
    301                 default:
    302                     skipTag();
    303             }
    304         }
    305     }
    306 
    307     public void parsePolicies() throws IOException {
    308         while (nextTag(Tags.PROVISION_POLICIES) != END) {
    309             if (tag == Tags.PROVISION_POLICY) {
    310                 parsePolicy();
    311             } else {
    312                 skipTag();
    313             }
    314         }
    315     }
    316 
    317     @Override
    318     public boolean parse() throws IOException {
    319         boolean res = false;
    320         if (nextTag(START_DOCUMENT) != Tags.PROVISION_PROVISION) {
    321             throw new IOException();
    322         }
    323         while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
    324             switch (tag) {
    325                 case Tags.PROVISION_STATUS:
    326                     int status = getValueInt();
    327                     mService.userLog("Provision status: ", status);
    328                     res = (status == 1);
    329                     break;
    330                 case Tags.PROVISION_POLICIES:
    331                     parsePolicies();
    332                     break;
    333                 case Tags.PROVISION_REMOTE_WIPE:
    334                     // Indicate remote wipe command received
    335                     mRemoteWipe = true;
    336                     break;
    337                 default:
    338                     skipTag();
    339             }
    340         }
    341         return res;
    342     }
    343 }
    344 
    345