-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathIP2LocationWebService.hs
162 lines (149 loc) · 5.39 KB
/
IP2LocationWebService.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
{-# LANGUAGE OverloadedStrings,TemplateHaskell #-}
{-|
Module : IP2LocationWebService
Description : IP2Location Haskell package
Copyright : (c) IP2Location, 2023 - 2024
License : MIT
Maintainer : sales@ip2location.com
Stability : experimental
This Haskell package allows users to query an IP address to get geolocation info.
IP2Location Web Service API subscription at https://www.ip2location.com/web-service/ip2location
-}
module IP2LocationWebService (WSResult(..), WSConfig, openWS, lookUp, getCredit) where
import Control.Exception
import System.Exit
import Data.Aeson as DA
import Data.Aeson.TH
import Network.HTTP.Client
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Network.HTTP.Types.Status (statusCode)
import Data.Maybe
import Network.URI.Encode as URIE
import Data.List.Split
import Data.ByteString.Lazy as BS (ByteString, unpack)
import Data.Char (chr)
-- | Contains the web service configuration.
data WSConfig = WSConfig {
-- | Web service API key
apiKey :: String,
-- | API package
apiPackage :: String,
-- | Use SSL
useSSL :: Bool
} deriving (Show)
-- | Contains the web service results.
data WSResult = WSResult {
-- | Response status or error
response :: String,
-- | Country code
country_code :: Maybe String,
-- | Country name
country_name :: Maybe String,
-- | Region name
region_name :: Maybe String,
-- | City name
city_name :: Maybe String,
-- | Latitude
latitude :: Maybe Float,
-- | Longitude
longitude :: Maybe Float,
-- | ZIP code
zip_code :: Maybe String,
-- | Time zone
time_zone :: Maybe String,
-- | ISP name
isp :: Maybe String,
-- | Domain
domain :: Maybe String,
-- | Net speed
net_speed :: Maybe String,
-- | IDD code
idd_code :: Maybe String,
-- | Area code
area_code :: Maybe String,
-- | Weather station code
weather_station_code :: Maybe String,
-- | Weather station name
weather_station_name :: Maybe String,
-- | MCC
mcc :: Maybe String,
-- | MNC
mnc :: Maybe String,
-- | Mobile brand
mobile_brand :: Maybe String,
-- | Elevation
elevation :: Maybe Float,
-- | Usage type
usage_type :: Maybe String,
-- | Address type
address_type :: Maybe String,
-- | IAB category code
category :: Maybe String,
-- | IAB category name
category_name :: Maybe String,
-- | Credits consumed
credits_consumed :: Maybe Float
} deriving (Show, Eq)
$(deriveJSON defaultOptions ''WSResult)
checkparams :: String -> String -> IO String
checkparams apikey apipackage = do
return "OK"
--- regex part commented out due to cabal dependency issues
-- let apikeyok = apikey =~ ("^[0-9A-Z]{10}$" :: String) :: Bool
-- if apikeyok == False
-- then die(show "Invalid API key.")
-- else do
-- let apipackageok = apipackage =~ ("^WS[0-9]+$" :: String) :: Bool
-- if apipackageok == False
-- then die(show "Invalid package name.")
-- else return "OK"
{-|
The 'openWS' function initializes the web service configuration.
It takes 3 arguments; the web service API key, the API package to call & whether to use SSL.
-}
openWS :: String -> String -> Bool -> IO WSConfig
openWS apikey apipackage usessl = do
paramok <- checkparams apikey apipackage
return (WSConfig apikey apipackage usessl)
{-|
The 'lookUp' function returns an WSResult containing geolocation data for an IP address
It takes 2 arguments; the web service configuration from 'openWS' function (WSConfig record), either IPv4 or IPv6 address (String)
-}
lookUp :: WSConfig -> String -> IO WSResult
lookUp myconfig ip = do
let key = apiKey myconfig
let package = apiPackage myconfig
let usessl = useSSL myconfig
paramok <- checkparams key package
let protocol = if usessl == True
then "https"
else "http"
manager <- newManager tlsManagerSettings
httprequest <- parseRequest $ protocol ++ "://api.ip2location.com/v2/?key=" ++ key ++ "&package=" ++ package ++ "&ip=" ++ (URIE.encode ip)
httpresponse <- httpLbs httprequest manager
let json = responseBody httpresponse
let Just result = DA.decode json :: Maybe WSResult
return result
bsToString :: BS.ByteString -> String
bsToString bs = map (chr . fromEnum) . BS.unpack $ bs
{-|
The 'getCredit' function returns an IO String containing web service credit balance for the API key.
It takes 1 argument; the web service configuration from 'openWS' function (WSConfig record).
-}
getCredit :: WSConfig -> IO String
getCredit myconfig = do
let key = apiKey myconfig
let package = apiPackage myconfig
let usessl = useSSL myconfig
paramok <- checkparams key package
let protocol = if usessl == True
then "https"
else "http"
manager <- newManager tlsManagerSettings
httprequest <- parseRequest $ protocol ++ "://api.ip2location.com/v2/?key=" ++ key ++ "&check=true"
httpresponse <- httpLbs httprequest manager
let json = responseBody httpresponse
-- using splitOn to extract the response field to bypass the Haskell duplicate field name issues
let part = head (splitOn "}" (bsToString json))
let result = last (splitOn ":" part)
return result