|
|
|
@ -11,15 +11,6 @@ import 'server.dart';
|
|
|
|
|
class ServerList extends StatefulWidget {
|
|
|
|
|
ServerList({Key key, this.title}) : super(key: key);
|
|
|
|
|
|
|
|
|
|
// This widget is the home page of your application. It is stateful, meaning
|
|
|
|
|
// that it has a State object (defined below) that contains fields that affect
|
|
|
|
|
// how it looks.
|
|
|
|
|
|
|
|
|
|
// This class is the configuration for the state. It holds the values (in this
|
|
|
|
|
// case the title) provided by the parent (in this case the App widget) and
|
|
|
|
|
// used by the build method of the State. Fields in a Widget subclass are
|
|
|
|
|
// always marked "final".
|
|
|
|
|
|
|
|
|
|
final String title;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@ -27,6 +18,7 @@ class ServerList extends StatefulWidget {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _ServerListState extends State<ServerList> {
|
|
|
|
|
// Currently expanded server list entry, -1 = no field is expanded
|
|
|
|
|
int selectedServer = -1;
|
|
|
|
|
List<Server> servers = [
|
|
|
|
|
new Server(
|
|
|
|
@ -44,7 +36,7 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
new Server(
|
|
|
|
|
"Raspberry Pi",
|
|
|
|
|
"https://pi.kescher.at/isup",
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
Timer timer;
|
|
|
|
@ -55,9 +47,8 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
super.initState();
|
|
|
|
|
|
|
|
|
|
_updateAllServers();
|
|
|
|
|
timer = new Timer.periodic(new Duration(seconds: 30), (timer) {
|
|
|
|
|
_updateAllServers();
|
|
|
|
|
});
|
|
|
|
|
timer = new Timer.periodic(
|
|
|
|
|
new Duration(seconds: 30), (_) => _updateAllServers());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _updateAllServers() async {
|
|
|
|
@ -65,11 +56,7 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _updateServerStatus(Server s) {
|
|
|
|
|
_checkStatus(s.uri).then((value) {
|
|
|
|
|
setState(() {
|
|
|
|
|
s.status = value;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
_checkStatus(s.uri).then((value) => setState(() => s.status = value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
@ -263,7 +250,7 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
context: context,
|
|
|
|
|
builder: (BuildContext context) {
|
|
|
|
|
return AlertDialog(
|
|
|
|
|
title: Text('Are you shure you want to delete ' +
|
|
|
|
|
title: Text('Are you sure you want to delete ' +
|
|
|
|
|
servers[index].displayName +
|
|
|
|
|
'?'),
|
|
|
|
|
content: Form(
|
|
|
|
@ -323,9 +310,7 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
}
|
|
|
|
|
return GestureDetector(
|
|
|
|
|
onTap: () {
|
|
|
|
|
setState(() {
|
|
|
|
|
selectedServer = index;
|
|
|
|
|
});
|
|
|
|
|
setState(() => selectedServer = index);
|
|
|
|
|
print(index.toString() + " clicked");
|
|
|
|
|
},
|
|
|
|
|
child: Container(
|
|
|
|
@ -368,8 +353,7 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Future<ServerStatus> _checkStatus(String uri,
|
|
|
|
|
{int iterations = 0}) async {
|
|
|
|
|
static Future<ServerStatus> _checkStatus(String uri) async {
|
|
|
|
|
Uri parsedUri;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
@ -378,126 +362,89 @@ class _ServerListState extends State<ServerList> {
|
|
|
|
|
return ServerStatus.dnserror;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (parsedUri.isScheme("http") || parsedUri.isScheme("https")) {
|
|
|
|
|
InternetAddress ia;
|
|
|
|
|
try {
|
|
|
|
|
// Check if host is IP address
|
|
|
|
|
InternetAddress(parsedUri.host);
|
|
|
|
|
} catch (ArgumentError) /* Host is a DNS name */ {
|
|
|
|
|
try {
|
|
|
|
|
ia = InternetAddress(parsedUri.host);
|
|
|
|
|
return await _checkResponse(parsedUri, iterations);
|
|
|
|
|
} catch (ArgumentError) /* Host is a DNS name */ {
|
|
|
|
|
try {
|
|
|
|
|
var v4recs =
|
|
|
|
|
await DnsUtils.lookupRecord(parsedUri.host, RRecordType.A);
|
|
|
|
|
var v6recs =
|
|
|
|
|
await DnsUtils.lookupRecord(parsedUri.host, RRecordType.AAAA);
|
|
|
|
|
var v4 = await _getDNS(v4recs, RRecordType.A);
|
|
|
|
|
var v6 = await _getDNS(v6recs, RRecordType.AAAA);
|
|
|
|
|
if (v4 != "" && v6 != "") {
|
|
|
|
|
return ServerStatus.dnserror;
|
|
|
|
|
}
|
|
|
|
|
} catch (SocketException) {
|
|
|
|
|
var v4recs = await DnsUtils.lookupRecord(parsedUri.host, RRecordType.A);
|
|
|
|
|
var v6recs =
|
|
|
|
|
await DnsUtils.lookupRecord(parsedUri.host, RRecordType.AAAA);
|
|
|
|
|
var v4 = await _resolveHostname(v4recs, RRecordType.A);
|
|
|
|
|
var v6 = await _resolveHostname(v6recs, RRecordType.AAAA);
|
|
|
|
|
if (v4 != "" && v6 != "") {
|
|
|
|
|
return ServerStatus.dnserror;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await _checkResponse(parsedUri, iterations);
|
|
|
|
|
} catch (SocketException) {
|
|
|
|
|
return ServerStatus.dnserror;
|
|
|
|
|
}
|
|
|
|
|
} else // non-HTTP scheme
|
|
|
|
|
return ServerStatus.unknown;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
return await _checkResponse(parsedUri);
|
|
|
|
|
} catch (TimeoutException) {
|
|
|
|
|
return ServerStatus.timeout;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Icon _generateIcon(Server server) {
|
|
|
|
|
var icon, color;
|
|
|
|
|
switch (server.status) {
|
|
|
|
|
case ServerStatus.unknown:
|
|
|
|
|
return new Icon(
|
|
|
|
|
Icons.sync,
|
|
|
|
|
color: Colors.blueGrey.shade400,
|
|
|
|
|
);
|
|
|
|
|
icon = Icons.timer;
|
|
|
|
|
color = Colors.blueGrey.shade400;
|
|
|
|
|
break;
|
|
|
|
|
case ServerStatus.ok:
|
|
|
|
|
return new Icon(
|
|
|
|
|
Icons.check_box,
|
|
|
|
|
color: Colors.green.shade400,
|
|
|
|
|
);
|
|
|
|
|
icon = Icons.check_box;
|
|
|
|
|
color = Colors.green.shade400;
|
|
|
|
|
break;
|
|
|
|
|
case ServerStatus.notok:
|
|
|
|
|
return new Icon(
|
|
|
|
|
Icons.indeterminate_check_box,
|
|
|
|
|
color: Colors.red,
|
|
|
|
|
);
|
|
|
|
|
icon = Icons.indeterminate_check_box;
|
|
|
|
|
color = Colors.red;
|
|
|
|
|
break;
|
|
|
|
|
case ServerStatus.timeout:
|
|
|
|
|
return new Icon(
|
|
|
|
|
Icons.timer_off,
|
|
|
|
|
color: Colors.yellowAccent.shade700,
|
|
|
|
|
);
|
|
|
|
|
icon = Icons.timer_off;
|
|
|
|
|
color = Colors.yellowAccent.shade700;
|
|
|
|
|
break;
|
|
|
|
|
case ServerStatus.dnserror:
|
|
|
|
|
return new Icon(
|
|
|
|
|
Icons.dns,
|
|
|
|
|
color: Colors.red.shade400,
|
|
|
|
|
);
|
|
|
|
|
icon = Icons.dns;
|
|
|
|
|
color = Colors.red.shade400;
|
|
|
|
|
break;
|
|
|
|
|
case ServerStatus.toomanyredirects:
|
|
|
|
|
return new Icon(
|
|
|
|
|
Icons.subdirectory_arrow_right,
|
|
|
|
|
color: Colors.red.shade400,
|
|
|
|
|
);
|
|
|
|
|
default:
|
|
|
|
|
icon = Icons.indeterminate_check_box;
|
|
|
|
|
color = Colors.red;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return new Icon(icon, color: color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static _getDNS(List<RRecord> records, RRecordType type) {
|
|
|
|
|
String _return;
|
|
|
|
|
static _resolveHostname(List<RRecord> records, RRecordType type) {
|
|
|
|
|
String ipAddress;
|
|
|
|
|
if (records == null) return ServerStatus.dnserror;
|
|
|
|
|
|
|
|
|
|
records.forEach((record) {
|
|
|
|
|
if (record != null) {
|
|
|
|
|
if (ipAddress != null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (record == null) {
|
|
|
|
|
if (ipAddress == null) {
|
|
|
|
|
// ignore: unrelated_type_equality_checks
|
|
|
|
|
if (record.rType == type) {
|
|
|
|
|
_return = record.data;
|
|
|
|
|
ipAddress = record.data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if (_return == null) _return = "";
|
|
|
|
|
return _return;
|
|
|
|
|
if (ipAddress == null) ipAddress = "";
|
|
|
|
|
return ipAddress;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Future<ServerStatus> _checkResponse(Uri uri, int iterations) async {
|
|
|
|
|
var scheme = uri.scheme;
|
|
|
|
|
var port = uri.port;
|
|
|
|
|
var host = uri.host;
|
|
|
|
|
var location = uri.path;
|
|
|
|
|
static Future<ServerStatus> _checkResponse(Uri uri) async {
|
|
|
|
|
var response = await HttpUtils.getForFullResponse(uri.toString())
|
|
|
|
|
.timeout(new Duration(seconds: 3));
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
|
var response = await HttpUtils.getForFullResponse(scheme + "://" + host,
|
|
|
|
|
headers: <String, String>{"Location": location})
|
|
|
|
|
.timeout(new Duration(seconds: 3));
|
|
|
|
|
|
|
|
|
|
if (response.statusCode > 199 && response.statusCode < 300) {
|
|
|
|
|
return ServerStatus.ok;
|
|
|
|
|
} else if (response.statusCode == HttpStatus.movedPermanently ||
|
|
|
|
|
response.statusCode == HttpStatus.found ||
|
|
|
|
|
response.statusCode == HttpStatus.temporaryRedirect ||
|
|
|
|
|
response.statusCode == HttpStatus.permanentRedirect) {
|
|
|
|
|
if (iterations < 5) return ServerStatus.toomanyredirects;
|
|
|
|
|
var responseLocation = response.headers["Location"];
|
|
|
|
|
if (responseLocation.startsWith("http://") ||
|
|
|
|
|
responseLocation.startsWith("https://")) {
|
|
|
|
|
return await _checkStatus(responseLocation,
|
|
|
|
|
iterations: iterations + 1);
|
|
|
|
|
}
|
|
|
|
|
location = responseLocation;
|
|
|
|
|
return await _checkStatus(
|
|
|
|
|
scheme + "://" + host + ":" + port.toString() + location,
|
|
|
|
|
iterations: iterations + 1);
|
|
|
|
|
} else {
|
|
|
|
|
return ServerStatus.notok;
|
|
|
|
|
}
|
|
|
|
|
if (response.statusCode > 199 && response.statusCode < 300) {
|
|
|
|
|
return ServerStatus.ok;
|
|
|
|
|
} else {
|
|
|
|
|
return ServerStatus.notok;
|
|
|
|
|
}
|
|
|
|
|
// Too many redirects
|
|
|
|
|
return ServerStatus.unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|