@@ -12,6 +12,9 @@ import 'dart:convert';
1212import 'dart:io' ;
1313
1414import 'package:flutter/material.dart' ;
15+ import 'package:http/io_client.dart' ;
16+ import 'package:monero_rpc/monero_rpc.dart' ;
17+ import 'package:digest_auth/digest_auth.dart' ;
1518import 'package:socks5_proxy/socks.dart' ;
1619import 'package:tor_ffi_plugin/socks_socket.dart' ;
1720
@@ -27,11 +30,18 @@ class MoneroNodeConnectionResponse {
2730 final int ? port;
2831 final bool success;
2932
30- MoneroNodeConnectionResponse (this .cert, this .url, this .port, this .success);
33+ MoneroNodeConnectionResponse (
34+ this .cert,
35+ this .url,
36+ this .port,
37+ this .success,
38+ );
3139}
3240
3341Future <MoneroNodeConnectionResponse > testMoneroNodeConnection (
3442 Uri uri,
43+ String ? username,
44+ String ? password,
3545 bool allowBadX509Certificate, {
3646 required ({
3747 InternetAddress host,
@@ -59,36 +69,37 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
5969 await socket.connect ();
6070 await socket.connectTo (uri.host, uri.port);
6171
62- final body = jsonEncode ({
63- "jsonrpc" : "2.0" ,
64- "id" : "0" ,
65- "method" : "get_info" ,
66- });
67-
68- final request = 'POST /json_rpc HTTP/1.1\r\n '
69- 'Host: ${uri .host }\r\n '
70- 'Content-Type: application/json\r\n '
71- 'Content-Length: ${body .length }\r\n '
72- '\r\n '
73- '$body ' ;
74-
75- socket.write (request);
76- print ("Request sent: $request " );
77-
78- final buffer = StringBuffer ();
79- await for (var response in socket.inputStream) {
80- buffer.write (utf8.decode (response));
81- if (buffer.toString ().contains ("\r\n\r\n " )) {
82- break ;
72+ final rawRequest = DaemonRpc .rawRequestRpc (uri, 'get_info' , {});
73+ var response = await socket.send (rawRequest);
74+ // check if we need authentication
75+ String ? authenticateHeaderValue;
76+ for (final line in response.split ('\r\n ' )) {
77+ if (line.contains ('WWW-authenticate: ' )) {
78+ // both the password and username needs to be
79+ if (username == null || password == null ) {
80+ // node asking us for authentication, but we don't have any crendentials.
81+ return MoneroNodeConnectionResponse (null , null , null , false );
82+ }
83+ authenticateHeaderValue =
84+ line.replaceFirst ('WWW-authenticate: ' , '' ).trim ();
8385 }
8486 }
85-
86- final result = buffer.toString ();
87- print ("Response received: $result " );
87+ // header to authenticate was present, we need to remake the request with digest
88+ if (authenticateHeaderValue != null ) {
89+ final digestAuth = DigestAuth (username! , password! );
90+ digestAuth.initFromAuthorizationHeader (authenticateHeaderValue);
91+
92+ // generate the Authorization header for the second request.
93+ final authHeader = digestAuth.getAuthString ('POST' , uri.path);
94+ final rawRequestAuthenticated =
95+ DaemonRpc .rawRequestRpc (uri, 'get_info' , {}, authHeader);
96+ // resend with an authenticated request
97+ response = await socket.send (rawRequestAuthenticated);
98+ }
8899
89100 // Check if the response contains "results" and does not contain "error"
90101 final success =
91- result .contains ('"result":' ) && ! result .contains ('"error"' );
102+ response .contains ('"result":' ) && ! response .contains ('"error"' );
92103
93104 return MoneroNodeConnectionResponse (null , null , null , success);
94105 } catch (e, s) {
@@ -124,36 +135,15 @@ Future<MoneroNodeConnectionResponse> testMoneroNodeConnection(
124135
125136 return false ;
126137 };
127-
128- final request = await httpClient.postUrl (uri);
129-
130- final body = utf8.encode (
131- jsonEncode ({
132- "jsonrpc" : "2.0" ,
133- "id" : "0" ,
134- "method" : "get_info" ,
135- }),
136- );
137-
138- request.headers.add (
139- 'Content-Length' ,
140- body.length.toString (),
141- preserveHeaderCase: true ,
142- );
143- request.headers.set (
144- 'Content-Type' ,
145- 'application/json' ,
146- preserveHeaderCase: true ,
138+ final daemonRpc = DaemonRpc (
139+ IOClient (httpClient),
140+ '$uri ' ,
141+ username: username,
142+ password: password,
147143 );
144+ final result = await daemonRpc.call ('get_info' , {});
148145
149- request.add (body);
150-
151- final response = await request.close ();
152- final result = await response.transform (utf8.decoder).join ();
153- // print("HTTP Response: $result");
154-
155- final success =
156- result.contains ('"result":' ) && ! result.contains ('"error"' );
146+ final success = result.containsKey ('status' ) && result['status' ] == 'OK' ;
157147
158148 return MoneroNodeConnectionResponse (null , null , null , success);
159149 } catch (e, s) {
@@ -210,3 +200,18 @@ Future<bool> showBadX509CertificateDialog(
210200
211201 return result ?? false ;
212202}
203+
204+ extension on SOCKSSocket {
205+ /// write the raw request to the socket and return the response as String
206+ Future <String > send (String rawRequest) async {
207+ write (rawRequest);
208+ final buffer = StringBuffer ();
209+ await for (final response in inputStream) {
210+ buffer.write (utf8.decode (response));
211+ if (buffer.toString ().contains ("\r\n\r\n " )) {
212+ break ;
213+ }
214+ }
215+ return buffer.toString ();
216+ }
217+ }
0 commit comments