mirror of
https://github.com/MengMengCode/VoCat.git
synced 2026-08-13 03:13:43 +08:00
74 lines
2.4 KiB
Go
74 lines
2.4 KiB
Go
package device
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"vocat/internal/i18n"
|
|
)
|
|
|
|
// BlockedMCCs lists the mobile country codes whose SIM cards must not be
|
|
// served by this product. The product does not provide service to mainland
|
|
// China cards; the set mirrors the CN entry of the MCC table used for upstream
|
|
// proxy routing (460/461). It is keyed by MCC with a display name for logs and
|
|
// user-facing messaging.
|
|
var BlockedMCCs = map[string]string{
|
|
"460": "中国",
|
|
"461": "中国",
|
|
}
|
|
|
|
// CardMCCMNC splits an IMSI into its mobile country code and mobile network
|
|
// code. The MCC is the leading three digits and the MNC the following two or
|
|
// three. Empty strings are returned for an unusable IMSI.
|
|
func CardMCCMNC(imsi string) (mcc string, mnc string) {
|
|
digits := strings.TrimSpace(imsi)
|
|
if len(digits) < 5 ||
|
|
strings.IndexFunc(digits, func(r rune) bool { return !unicode.IsDigit(r) }) >= 0 {
|
|
return "", ""
|
|
}
|
|
mcc = digits[:3]
|
|
mnc = digits[3:]
|
|
if len(mnc) > 3 {
|
|
mnc = mnc[:3]
|
|
}
|
|
return mcc, mnc
|
|
}
|
|
|
|
// RegionBlockReason returns a human-readable reason when the SIM identified by
|
|
// the IMSI belongs to a blocked region. It returns an empty string when the
|
|
// card is allowed or when the IMSI is unavailable: only a confirmed blocked
|
|
// MCC triggers a block (fail-open), so a transient IMSI read failure never
|
|
// denies service to a legitimate card.
|
|
func RegionBlockReason(imsi string) string {
|
|
mcc, _ := CardMCCMNC(imsi)
|
|
country, blocked := BlockedMCCs[mcc]
|
|
if !blocked {
|
|
return ""
|
|
}
|
|
return i18n.Tf("SIM 卡归属地为%s(MCC %s),本服务不向该地区卡片提供数据/短信/VoWiFi", i18n.T(country), mcc)
|
|
}
|
|
|
|
// regionBlockError reports whether the currently inserted SIM must not be
|
|
// served. It reads only the cached snapshot IMSI and never issues an extra AT
|
|
// command, so it adds no modem round-trip and leaves guarded command
|
|
// transcripts untouched. A missing snapshot or IMSI yields nil (fail-open);
|
|
// the periodic region enforcement forces airplane mode as the authoritative
|
|
// backstop.
|
|
func (manager *Manager) regionBlockError(state *managedDevice) error {
|
|
manager.mu.RLock()
|
|
var snapshot *Snapshot
|
|
if state.snapshot != nil {
|
|
value := *state.snapshot
|
|
snapshot = &value
|
|
}
|
|
manager.mu.RUnlock()
|
|
if snapshot == nil {
|
|
return nil
|
|
}
|
|
if reason := RegionBlockReason(snapshot.IMSI); reason != "" {
|
|
return fmt.Errorf("%w: %s", ErrRegionBlocked, reason)
|
|
}
|
|
return nil
|
|
}
|