/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.captiveportal; import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE; import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE; import android.text.TextUtils; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.net.MalformedURLException; import java.net.URL; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** @hide */ public abstract class CaptivePortalProbeSpec { private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName(); private static final String REGEX_SEPARATOR = "@@/@@"; private static final String SPEC_SEPARATOR = "@@,@@"; private final String mEncodedSpec; private final URL mUrl; CaptivePortalProbeSpec(@NonNull String encodedSpec, @NonNull URL url) { mEncodedSpec = checkNotNull(encodedSpec); mUrl = checkNotNull(url); } /** * Parse a {@link CaptivePortalProbeSpec} from a {@link String}. * *
The valid format is a URL followed by two regular expressions, each separated by "@@/@@". * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}. * @throws ParseException The string is empty, does not match the above format, or a regular * expression is invalid for {@link Pattern#compile(String)}. * @hide */ @VisibleForTesting @NonNull public static CaptivePortalProbeSpec parseSpec(@NonNull String spec) throws ParseException, MalformedURLException { if (TextUtils.isEmpty(spec)) { throw new ParseException("Empty probe spec", 0 /* errorOffset */); } String[] splits = TextUtils.split(spec, REGEX_SEPARATOR); if (splits.length != 3) { throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */); } final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length(); final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length(); final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos); final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos); return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex); } @Nullable private static Pattern parsePatternIfNonEmpty(@Nullable String pattern, int pos) throws ParseException { if (TextUtils.isEmpty(pattern)) { return null; } try { return Pattern.compile(pattern); } catch (PatternSyntaxException e) { throw new ParseException( String.format("Invalid status pattern [%s]: %s", pattern, e), pos /* errorOffset */); } } /** * Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec * based on the status code of the provided URL if the spec cannot be parsed. */ @Nullable public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) { if (spec != null) { try { return parseSpec(spec); } catch (ParseException | MalformedURLException e) { Log.e(TAG, "Invalid probe spec: " + spec, e); // Fall through } } return null; } /** * Parse a config String to build an array of {@link CaptivePortalProbeSpec}. * *
Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}. *
This method does not throw but ignores any entry that could not be parsed.
*/
@NonNull
public static Collection