-
Notifications
You must be signed in to change notification settings - Fork 474
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changed: Expose a unix server socket
So clients can connect without having to call am if the plugin is already running.
- Loading branch information
1 parent
5ca50e0
commit 4c8296e
Showing
4 changed files
with
244 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
package com.termux.api; | ||
|
||
import android.app.Application; | ||
import android.content.Intent; | ||
import android.net.LocalServerSocket; | ||
import android.net.LocalSocket; | ||
|
||
import com.termux.api.util.TermuxApiLogger; | ||
|
||
import java.io.BufferedWriter; | ||
import java.io.DataInputStream; | ||
import java.io.OutputStreamWriter; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class App extends Application | ||
{ | ||
public static final String LISTEN_ADDRESS = "com.termux.api://listen"; | ||
private static final Pattern EXTRA_STRING = Pattern.compile("(-e|--es|--esa) +([^ ]+) +\"(.*?)(?<!\\\\)\"", Pattern.DOTALL); | ||
private static final Pattern EXTRA_BOOLEAN = Pattern.compile("--ez +([^ ]+) +([^ ]+)"); | ||
private static final Pattern EXTRA_INT = Pattern.compile("--ei +([^ ]+) +(-?[0-9]+)"); | ||
private static final Pattern EXTRA_FLOAT = Pattern.compile("--ef +([^ ]+) +(-?[0-9]+(?:\\.[0-9]+))"); | ||
private static final Pattern EXTRA_INT_LIST = Pattern.compile("--eia +([^ ]+) +(-?[0-9]+(?:,-?[0-9]+)*)"); | ||
private static final Pattern EXTRA_LONG_LIST = Pattern.compile("--ela +([^ ]+) +(-?[0-9]+(?:,-?[0-9]+)*)"); | ||
private static final Pattern EXTRA_UNSUPPORTED = Pattern.compile("--e[^izs ] +[^ ]+ +[^ ]+"); | ||
|
||
|
||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
new Thread(() -> { | ||
try (LocalServerSocket listen = new LocalServerSocket(LISTEN_ADDRESS)) { | ||
while (true) { | ||
try (LocalSocket con = listen.accept(); | ||
DataInputStream in = new DataInputStream(con.getInputStream()); | ||
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(con.getOutputStream()))) { | ||
// only accept connections from Termux programs | ||
if (con.getPeerCredentials().getUid() != getApplicationInfo().uid) { | ||
continue; | ||
} | ||
try { | ||
//System.out.println("connection"); | ||
int length = in.readUnsignedShort(); | ||
byte[] b = new byte[length]; | ||
in.readFully(b); | ||
String cmdline = new String(b, StandardCharsets.UTF_8); | ||
|
||
//System.out.println(cmdline.replaceAll("--es socket_input \".*?\"","").replaceAll("--es socket_output \".*?\"","")); | ||
HashMap<String, String> stringExtras = new HashMap<>(); | ||
HashMap<String, String[]> stringArrayExtras = new HashMap<>(); | ||
HashMap<String, Boolean> booleanExtras = new HashMap<>(); | ||
HashMap<String, Integer> intExtras = new HashMap<>(); | ||
HashMap<String, Float> floatExtras = new HashMap<>(); | ||
HashMap<String, int[]> intArrayExtras = new HashMap<>(); | ||
HashMap<String, long[]> longArrayExtras = new HashMap<>(); | ||
boolean err = false; | ||
|
||
// extract and remove the string extras first, so another argument embedded in a string isn't counted as an argument | ||
Matcher m = EXTRA_STRING.matcher(cmdline); | ||
while (m.find()) { | ||
String option = m.group(1); | ||
if ("-e".equals(option) || "--es".equals(option)) { | ||
// unescape " | ||
stringExtras.put(m.group(2), Objects.requireNonNull(m.group(3)).replaceAll("\\\\\"","\"")); | ||
} else { | ||
// split the list | ||
String[] list = Objects.requireNonNull(m.group(3)).split("(?<!\\\\),"); | ||
for (int i = 0; i<list.length; i++) { | ||
/// unescape the "," | ||
list[i] = list[i].replaceFirst("\\\\,", ","); | ||
} | ||
stringArrayExtras.put(m.group(2), list); | ||
} | ||
|
||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
m = EXTRA_BOOLEAN.matcher(cmdline); | ||
while (m.find()) { | ||
booleanExtras.put(m.group(1), Boolean.parseBoolean(m.group(2))); | ||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
m = EXTRA_INT.matcher(cmdline); | ||
while (m.find()) { | ||
try { | ||
intExtras.put(m.group(1), Integer.parseInt(Objects.requireNonNull(m.group(2)))); | ||
} catch (NumberFormatException e) { | ||
String msg = "Invalid integer extra: " + m.group(0) + "\n"; | ||
TermuxApiLogger.info(msg); | ||
out.write(msg); | ||
err = true; | ||
break; | ||
} | ||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
m = EXTRA_FLOAT.matcher(cmdline); | ||
while (m.find()) { | ||
try { | ||
floatExtras.put(m.group(1), Float.parseFloat(Objects.requireNonNull(m.group(2)))); | ||
} catch (NumberFormatException e) { | ||
String msg = "Invalid float extra: " + m.group(0) + "\n"; | ||
TermuxApiLogger.info(msg); | ||
out.write(msg); | ||
err = true; | ||
break; | ||
} | ||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
m = EXTRA_INT_LIST.matcher(cmdline); | ||
while (m.find()) { | ||
try { | ||
String[] parts = Objects.requireNonNull(m.group(2)).split(","); | ||
int[] ints = new int[parts.length]; | ||
for (int i = 0; i<parts.length; i++) { | ||
ints[i] = Integer.parseInt(parts[i]); | ||
} | ||
intArrayExtras.put(m.group(1), ints); | ||
} catch (NumberFormatException e) { | ||
String msg = "Invalid int array extra: " + m.group(0) + "\n"; | ||
TermuxApiLogger.info(msg); | ||
out.write(msg); | ||
err = true; | ||
break; | ||
} | ||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
m = EXTRA_LONG_LIST.matcher(cmdline); | ||
while (m.find()) { | ||
try { | ||
String[] parts = Objects.requireNonNull(m.group(2)).split(","); | ||
long[] longs = new long[parts.length]; | ||
for (int i = 0; i<parts.length; i++) { | ||
longs[i] = Long.parseLong(parts[i]); | ||
} | ||
longArrayExtras.put(m.group(1), longs); | ||
} catch (NumberFormatException e) { | ||
String msg = "Invalid long array extra: " + m.group(0) + "\n"; | ||
TermuxApiLogger.info(msg); | ||
out.write(msg); | ||
err = true; | ||
break; | ||
} | ||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
m = EXTRA_UNSUPPORTED.matcher(cmdline); | ||
if (m.find()) { | ||
String msg = "Unsupported argument type: " + m.group(0) + "\n"; | ||
TermuxApiLogger.info(msg); | ||
out.write(msg); | ||
err = true; | ||
} | ||
cmdline = m.replaceAll(""); | ||
|
||
// check if there are any non-whitespace characters left after parsing all the options | ||
cmdline = cmdline.replaceAll("\\s", ""); | ||
if (! "".equals(cmdline)) { | ||
String msg = "Unsupported options: " + cmdline + "\n"; | ||
TermuxApiLogger.info(msg); | ||
out.write(msg); | ||
err = true; | ||
} | ||
|
||
if (err) { | ||
out.flush(); | ||
continue; | ||
} | ||
|
||
// construct the intent with the extras | ||
Intent i = new Intent(getApplicationContext(), TermuxApiReceiver.class); | ||
for (Map.Entry<String, String> e : stringExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
for (Map.Entry<String, String[]> e : stringArrayExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
for (Map.Entry<String, Integer> e : intExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
for (Map.Entry<String, Boolean> e : booleanExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
for (Map.Entry<String, Float> e : floatExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
for (Map.Entry<String, int[]> e : intArrayExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
for (Map.Entry<String, long[]> e : longArrayExtras.entrySet()) { | ||
i.putExtra(e.getKey(), e.getValue()); | ||
} | ||
getApplicationContext().sendOrderedBroadcast(i, null); | ||
// send a null byte as a sign that the arguments have been successfully received, parsed and the broadcast receiver is called | ||
con.getOutputStream().write(0); | ||
con.getOutputStream().flush(); | ||
} catch (Exception e) { | ||
TermuxApiLogger.error("Error parsing arguments", e); | ||
out.write("Exception in the plugin\n"); | ||
out.flush(); | ||
} | ||
} | ||
} | ||
} catch (Exception e) { | ||
TermuxApiLogger.error("Error listening for connections", e); | ||
} | ||
}).start(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.termux.api; | ||
|
||
import android.app.Service; | ||
import android.content.Intent; | ||
import android.os.IBinder; | ||
|
||
import androidx.annotation.Nullable; | ||
|
||
public class KeepAliveService extends Service | ||
{ | ||
@Override | ||
public int onStartCommand(Intent intent, int flags, int startId) { | ||
return Service.START_STICKY; | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public IBinder onBind(Intent intent) { | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters