diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 9d4c2fa..0000000 --- a/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[*.dart] -indent_size = 2 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 09360f3..0000000 --- a/.gitignore +++ /dev/null @@ -1,51 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release -native/target -xcuserdata -jniLibs - -Cargo.lock -pubspec.lock diff --git a/.metadata b/.metadata deleted file mode 100644 index 8e89d4d..0000000 --- a/.metadata +++ /dev/null @@ -1,45 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled. - -version: - revision: 9944297138845a94256f1cf37beb88ff9a8e811a - channel: stable - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - platform: android - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - platform: ios - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - platform: linux - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - platform: macos - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - platform: web - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - platform: windows - create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index fd85870..0000000 --- a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -java liberica-8u322+6 diff --git a/AssignmentSolution.png b/AssignmentSolution.png new file mode 100644 index 0000000..e0cf69e Binary files /dev/null and b/AssignmentSolution.png differ diff --git a/README.md b/README.md index 8bbeb11..06efd7c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-24ddc0f5d75046c5622901739e7c5dd533143b0c8e959d652212380cedb1ea36.svg)](https://classroom.github.com/a/6xWVd6R2) # Information ![rustdesk-banner](https://user-images.githubusercontent.com/71636191/236513788-89da3f2a-6898-4e30-a12f-b5af129858c3.png) @@ -47,7 +48,7 @@ The starter code is included in this repository. ‼️ * Prepare your Linux environment * Follow https://github.com/Desdaemon/flutter_rust_bridge_template/blob/main/README.md to get started * `flutter run -d linux` -![image](https://user-images.githubusercontent.com/71636191/231404421-a203e923-0c51-42fd-9ee7-cea0ea44fdd9.png) +![image](./AssignmentSolution.png) ### Task instructions diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index fadd6b6..98f87a8 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -1,13 +1,12 @@ // AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.62.1. -// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member +// Generated by `flutter_rust_bridge`@ 1.77.1. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const -import 'bridge_generated.io.dart' - if (dart.library.html) 'bridge_generated.web.dart'; import 'dart:convert'; import 'dart:async'; import 'package:meta/meta.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; abstract class Native { Future platform({dynamic hint}); @@ -17,6 +16,10 @@ abstract class Native { Future rustReleaseMode({dynamic hint}); FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta; + + Future elevateLsCommand({required String password, dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kElevateLsCommandConstMeta; } enum Platform { diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 7501e4b..b9bf9c1 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -1,14 +1,21 @@ // AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.62.1. -// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member +// Generated by `flutter_rust_bridge`@ 1.77.1. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const import "bridge_definitions.dart"; import 'dart:convert'; import 'dart:async'; import 'package:meta/meta.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; -import 'bridge_generated.io.dart' - if (dart.library.html) 'bridge_generated.web.dart'; +import 'package:uuid/uuid.dart'; + +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +import 'dart:ffi' as ffi; class NativeImpl implements Native { final NativePlatform _platform; @@ -51,11 +58,32 @@ class NativeImpl implements Native { argNames: [], ); + Future elevateLsCommand({required String password, dynamic hint}) { + var arg0 = _platform.api2wire_String(password); + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_elevate_ls_command(port_, arg0), + parseSuccessData: _wire2api_String, + constMeta: kElevateLsCommandConstMeta, + argValues: [password], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kElevateLsCommandConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "elevate_ls_command", + argNames: ["password"], + ); + void dispose() { _platform.dispose(); } // Section: wire2api + String _wire2api_String(dynamic raw) { + return raw as String; + } + bool _wire2api_bool(dynamic raw) { return raw as bool; } @@ -65,10 +93,229 @@ class NativeImpl implements Native { } Platform _wire2api_platform(dynamic raw) { - return Platform.values[raw]; + return Platform.values[raw as int]; + } + + int _wire2api_u8(dynamic raw) { + return raw as int; + } + + Uint8List _wire2api_uint_8_list(dynamic raw) { + return raw as Uint8List; } } // Section: api2wire +@protected +int api2wire_u8(int raw) { + return raw; +} + // Section: finalizer + +class NativePlatform extends FlutterRustBridgeBase { + NativePlatform(ffi.DynamicLibrary dylib) : super(NativeWire(dylib)); + +// Section: api2wire + + @protected + ffi.Pointer api2wire_String(String raw) { + return api2wire_uint_8_list(utf8.encoder.convert(raw)); + } + + @protected + ffi.Pointer api2wire_uint_8_list(Uint8List raw) { + final ans = inner.new_uint_8_list_0(raw.length); + ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); + return ans; + } +// Section: finalizer + +// Section: api_fill_to_wire +} + +// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint + +/// generated by flutter_rust_bridge +class NativeWire implements FlutterRustBridgeWireBase { + @internal + late final dartApi = DartApiDl(init_frb_dart_api_dl); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeWire.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + void store_dart_post_cobject( + DartPostCObjectFnType ptr, + ) { + return _store_dart_post_cobject( + ptr, + ); + } + + late final _store_dart_post_cobjectPtr = + _lookup>( + 'store_dart_post_cobject'); + late final _store_dart_post_cobject = _store_dart_post_cobjectPtr + .asFunction(); + + Object get_dart_object( + int ptr, + ) { + return _get_dart_object( + ptr, + ); + } + + late final _get_dart_objectPtr = + _lookup>( + 'get_dart_object'); + late final _get_dart_object = + _get_dart_objectPtr.asFunction(); + + void drop_dart_object( + int ptr, + ) { + return _drop_dart_object( + ptr, + ); + } + + late final _drop_dart_objectPtr = + _lookup>( + 'drop_dart_object'); + late final _drop_dart_object = + _drop_dart_objectPtr.asFunction(); + + int new_dart_opaque( + Object handle, + ) { + return _new_dart_opaque( + handle, + ); + } + + late final _new_dart_opaquePtr = + _lookup>( + 'new_dart_opaque'); + late final _new_dart_opaque = + _new_dart_opaquePtr.asFunction(); + + int init_frb_dart_api_dl( + ffi.Pointer obj, + ) { + return _init_frb_dart_api_dl( + obj, + ); + } + + late final _init_frb_dart_api_dlPtr = + _lookup)>>( + 'init_frb_dart_api_dl'); + late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr + .asFunction)>(); + + void wire_platform( + int port_, + ) { + return _wire_platform( + port_, + ); + } + + late final _wire_platformPtr = + _lookup>( + 'wire_platform'); + late final _wire_platform = + _wire_platformPtr.asFunction(); + + void wire_rust_release_mode( + int port_, + ) { + return _wire_rust_release_mode( + port_, + ); + } + + late final _wire_rust_release_modePtr = + _lookup>( + 'wire_rust_release_mode'); + late final _wire_rust_release_mode = + _wire_rust_release_modePtr.asFunction(); + + void wire_elevate_ls_command( + int port_, + ffi.Pointer password, + ) { + return _wire_elevate_ls_command( + port_, + password, + ); + } + + late final _wire_elevate_ls_commandPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, + ffi.Pointer)>>('wire_elevate_ls_command'); + late final _wire_elevate_ls_command = _wire_elevate_ls_commandPtr + .asFunction)>(); + + ffi.Pointer new_uint_8_list_0( + int len, + ) { + return _new_uint_8_list_0( + len, + ); + } + + late final _new_uint_8_list_0Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int32)>>('new_uint_8_list_0'); + late final _new_uint_8_list_0 = _new_uint_8_list_0Ptr + .asFunction Function(int)>(); + + void free_WireSyncReturn( + WireSyncReturn ptr, + ) { + return _free_WireSyncReturn( + ptr, + ); + } + + late final _free_WireSyncReturnPtr = + _lookup>( + 'free_WireSyncReturn'); + late final _free_WireSyncReturn = + _free_WireSyncReturnPtr.asFunction(); +} + +final class _Dart_Handle extends ffi.Opaque {} + +final class wire_uint_8_list extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + +typedef DartPostCObjectFnType = ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(DartPort port_id, ffi.Pointer message)>>; +typedef DartPort = ffi.Int64; diff --git a/lib/main.dart b/lib/main.dart index a334127..a5c6001 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,7 +15,7 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: const MyHomePage(title: 'Elevate priviledge to run a Linux command'), + home: const MyHomePage(title: 'Elevate privilege to run a Linux command'), ); } } @@ -29,6 +29,49 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { + late Future _futureFiles; + late TextEditingController _passwordController; + + @override + void initState() { + super.initState(); + _passwordController = TextEditingController(); + _futureFiles = Future.value(''); + } + + Future _fetchFiles(String password) async { + print("Fetching files"); + final commandOutput = await api.elevateLsCommand(password: password); + return commandOutput; + } + + Future _requestPassword(BuildContext context) async { + return showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Permission denied'), + content: TextField( + controller: _passwordController, + obscureText: true, + decoration: const InputDecoration(hintText: "Enter your password"), + ), + actions: [ + TextButton( + child: const Text('OK'), + onPressed: () { + Navigator.of(context).pop(); + setState(() { + _futureFiles = _fetchFiles(_passwordController.text); + }); + }, + ), + ], + ); + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -40,10 +83,27 @@ class _MyHomePageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("ls -la /root/", style: TextStyle(fontSize: 40.0)), - const Text('Run above cmd with Rust and print the output here'), + FutureBuilder( + future: _futureFiles, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return CircularProgressIndicator(); + } else if (snapshot.hasData) { + return Text(snapshot.data!); + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return SizedBox(); + } + }, + ), ], ), ), + floatingActionButton: FloatingActionButton( + onPressed: () => _requestPassword(context), + child: const Icon(Icons.lock), + ), ); } } diff --git a/native/Cargo.toml b/native/Cargo.toml index 667e107..82577d1 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -11,3 +11,4 @@ crate-type = ["cdylib", "staticlib"] [dependencies] anyhow = "1" flutter_rust_bridge = "1" +which = "3.1.0" diff --git a/native/src/api.rs b/native/src/api.rs index 976bb9d..0ee9a87 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -2,6 +2,10 @@ // When adding new code to your project, note that only items used // here will be transformed to their Dart equivalents. +use std::process::Command; +use std::str; +use std::{fs}; + // A plain enum without any fields. This is similar to Dart- or C-style enums. // flutter_rust_bridge is capable of generating code for enums with fields // (@freezed classes in Dart and tagged unions in C). @@ -57,3 +61,28 @@ pub fn platform() -> Platform { pub fn rust_release_mode() -> bool { cfg!(not(debug_assertions)) } + + +pub fn elevate_ls_command( password: String) -> String { + + let echo_cmd = format!("echo {}", password); + let output = Command::new("sh") + .arg("-c") + .arg(format!("{} | sudo -S ls -la /root > /tmp/output.txt", echo_cmd)) + .output(); + + + match output { + Ok(output) => { + let result = fs::read_to_string("/tmp/output.txt") + .expect("Failed to read output file"); + println!("ls command result: {}", result); + result + } + Err(error) => { + eprintln!("Failed to execute command: {}", error); + String::new() // Return an empty string or handle the error accordingly + } + } + +} \ No newline at end of file diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index a4ec960..8e4c5e0 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -11,14 +11,50 @@ pub extern "C" fn wire_rust_release_mode(port_: i64) { wire_rust_release_mode_impl(port_) } +#[no_mangle] +pub extern "C" fn wire_elevate_ls_command(port_: i64, password: *mut wire_uint_8_list) { + wire_elevate_ls_command_impl(port_, password) +} + // Section: allocate functions +#[no_mangle] +pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { + let ans = wire_uint_8_list { + ptr: support::new_leak_vec_ptr(Default::default(), len), + len, + }; + support::new_leak_box_ptr(ans) +} + // Section: related functions // Section: impl Wire2Api +impl Wire2Api for *mut wire_uint_8_list { + fn wire2api(self) -> String { + let vec: Vec = self.wire2api(); + String::from_utf8_lossy(&vec).into_owned() + } +} + +impl Wire2Api> for *mut wire_uint_8_list { + fn wire2api(self) -> Vec { + unsafe { + let wrap = support::box_from_leak_ptr(self); + support::vec_from_leak_ptr(wrap.ptr, wrap.len) + } + } +} // Section: wire structs +#[repr(C)] +#[derive(Clone)] +pub struct wire_uint_8_list { + ptr: *mut u8, + len: i32, +} + // Section: impl NewWithNullPtr pub trait NewWithNullPtr { diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 9f63b66..f91db93 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -9,7 +9,7 @@ clippy::too_many_arguments )] // AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.62.1. +// Generated by `flutter_rust_bridge`@ 1.77.1. use crate::api::*; use core::panic::UnwindSafe; @@ -41,6 +41,19 @@ fn wire_rust_release_mode_impl(port_: MessagePort) { move || move |task_callback| Ok(rust_release_mode()), ) } +fn wire_elevate_ls_command_impl(port_: MessagePort, password: impl Wire2Api + UnwindSafe) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap( + WrapInfo { + debug_name: "elevate_ls_command", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || { + let api_password = password.wire2api(); + move |task_callback| Ok(elevate_ls_command(api_password)) + }, + ) +} // Section: wrapper structs // Section: static checks @@ -63,6 +76,13 @@ where (!self.is_null()).then(|| self.wire2api()) } } + +impl Wire2Api for u8 { + fn wire2api(self) -> u8 { + self + } +} + // Section: impl IntoDart impl support::IntoDart for Platform { @@ -80,19 +100,14 @@ impl support::IntoDart for Platform { .into_dart() } } +impl support::IntoDartExceptPrimitive for Platform {} + // Section: executor support::lazy_static! { pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); } -/// cbindgen:ignore -#[cfg(target_family = "wasm")] -#[path = "bridge_generated.web.rs"] -mod web; -#[cfg(target_family = "wasm")] -pub use web::*; - #[cfg(not(target_family = "wasm"))] #[path = "bridge_generated.io.rs"] mod io; diff --git a/pubspec.yaml b/pubspec.yaml index e7f3e0b..8efc7de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.17.5 <3.0.0" + sdk: ^3.0.0 # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -34,9 +34,10 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - ffi: ^2.0.1 - flutter_rust_bridge: ^1.45.0 + ffi: ^2.0.2 + flutter_rust_bridge: ^1.77.1 meta: ^1.8.0 + freezed_annotation: ^2.2.0 dev_dependencies: flutter_test: @@ -48,7 +49,9 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 - ffigen: ^7.2.4 + ffigen: ^8.0.2 + build_runner: ^2.4.5 + freezed: ^2.3.5 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec