diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..589e214 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "server/php/example/httpserver"] + path = server/php/example/httpserver + url = git://github.com/youngj/httpserver.git diff --git a/res/menu/mainmenu.xml b/res/menu/mainmenu.xml index 4c3ea24..cc7148d 100755 --- a/res/menu/mainmenu.xml +++ b/res/menu/mainmenu.xml @@ -6,4 +6,7 @@ + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index b3b6094..8dc1820 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3,4 +3,5 @@ KalSMS 2 Settings Test + Check Now diff --git a/server/php/KalSMS.php b/server/php/KalSMS.php new file mode 100755 index 0000000..3c9a39d --- /dev/null +++ b/server/php/KalSMS.php @@ -0,0 +1,177 @@ +compute_signature($full_url, $_POST, $correct_password); + + //error_log("Correct signature: '$correct_signature'"); + + return $signature === $correct_signature; + } + + function compute_signature($url, $data, $password) + { + ksort($data); + + $input = $url; + foreach($data as $key => $value) + $input .= ",$key=$value"; + + $input .= ",$password"; + + //error_log("Signed data: '$input'"); + + return base64_encode(sha1($input, true)); + } +} + +class KalSMS_OutgoingMessage +{ + public $id = ''; + public $to; + public $message; +} + +class KalSMS_Action +{ + public $type; + public $kalsms; + + function __construct($kalsms) + { + $this->kalsms = $kalsms; + } +} + +class KalSMS_Action_Test extends KalSMS_Action +{ + function __construct($kalsms) + { + parent::__construct($kalsms); + $this->type = KalSMS::ACTION_TEST; + } +} + +class KalSMS_Action_Incoming extends KalSMS_Action +{ + public $from; + public $message; + + function __construct($kalsms) + { + parent::__construct($kalsms); + $this->type = KalSMS::ACTION_INCOMING; + $this->from = $_POST['from']; + $this->message = $_POST['message']; + } + + function get_response_xml($messages) + { + ob_start(); + echo "\n"; + echo ""; + foreach ($messages as $message) + { + echo "".KalSMS::escape($message->message).""; + } + echo ""; + return ob_get_clean(); + } +} + +class KalSMS_Action_Outgoing extends KalSMS_Action +{ + function __construct($kalsms) + { + parent::__construct($kalsms); + $this->type = KalSMS::ACTION_OUTGOING; + } + + function get_response_xml($messages) + { + ob_start(); + echo "\n"; + echo ""; + foreach ($messages as $message) + { + echo "". + KalSMS::escape($message->message).""; + } + echo ""; + return ob_get_clean(); + } +} + +class KalSMS_Action_SendStatus extends KalSMS_Action +{ + public $status; + public $id; + + function __construct($type) + { + $this->type = KalSMS::ACTION_SEND_STATUS; + $this->status = (int)$_POST['status']; + $this->id = $_POST['id']; + } +} \ No newline at end of file diff --git a/server/php/README.txt b/server/php/README.txt new file mode 100755 index 0000000..cabf434 --- /dev/null +++ b/server/php/README.txt @@ -0,0 +1,25 @@ + +PHP server library for KalSMS, with example implementation + +The KalSMS.php library is intended to be used as part of a PHP application +running on an HTTP server that receives incoming SMS messages from, and sends +outgoing SMS messages to an Android phone running KalSMS. + +To run the example implementation, the example/www/ directory should be made available +via a web server running PHP (e.g. Apache). You can also use the included standalone +PHP web server, by running the following commands: + git submodule init + php server.php + +example/config.php contains the list of phone numbers and passwords for phones running KalSMS. + +On a phone running KalSMS, go to Menu -> Settings and enter: + * Server URL: The URL to example/www/index.php. + If you're using server.php, this will be http://:8002/ + * Your phone number: One of the phone numbers listed in example/config.php + * Password: The corresponding password in example/config.php + +To send an outgoing SMS, use + php example/send_sms.php + +See KalSMS.php and example/www/index.php \ No newline at end of file diff --git a/server/php/example/config.php b/server/php/example/config.php new file mode 100755 index 0000000..6a6cadf --- /dev/null +++ b/server/php/example/config.php @@ -0,0 +1,9 @@ + 'rosebud', + '16505551213' => 's3krit', +); +$PHONE_NUMBERS = array_keys($PASSWORDS); + +$OUTGOING_DIR_NAME = __DIR__."/outgoing_sms"; \ No newline at end of file diff --git a/server/php/example/httpserver b/server/php/example/httpserver new file mode 160000 index 0000000..95a38ab --- /dev/null +++ b/server/php/example/httpserver @@ -0,0 +1 @@ +Subproject commit 95a38ab08f14ac54dc64b6f6c008d7f69af8a8f4 diff --git a/server/php/example/send_sms.php b/server/php/example/send_sms.php new file mode 100755 index 0000000..821f638 --- /dev/null +++ b/server/php/example/send_sms.php @@ -0,0 +1,41 @@ +] \"\""); + error_log("Examples: "); + error_log(" php send_sms.php 16505551212 16504449876 \"hello world\""); + error_log(" php send_sms.php 16504449876 \"hello world\""); + die; +} + +$id = uniqid(""); + +$filename = "$OUTGOING_DIR_NAME/$id.json"; + +file_put_contents($filename, json_encode(array( + 'from' => $from, + 'to' => $to, + 'message' => $message, + 'id' => $id +))); + +echo "Message $id added to outgoing queue\n"; \ No newline at end of file diff --git a/server/php/example/server.php b/server/php/example/server.php new file mode 100755 index 0000000..1eab65f --- /dev/null +++ b/server/php/example/server.php @@ -0,0 +1,46 @@ + 8002, + )); + } + + function route_request($request) + { + $uri = $request->uri; + + $doc_root = __DIR__ . '/www'; + + if (preg_match('#/$#', $uri)) + { + $uri .= "index.php"; + } + + if (preg_match('#\.php$#', $uri)) + { + return $this->get_php_response($request, "$doc_root$uri"); + } + else + { + return $this->get_static_response($request, "$doc_root$uri"); + } + } +} + +$server = new ExampleServer(); +$server->run_forever(); \ No newline at end of file diff --git a/server/php/example/test_sms.php b/server/php/example/test_sms.php new file mode 100755 index 0000000..d56d634 --- /dev/null +++ b/server/php/example/test_sms.php @@ -0,0 +1,6 @@ +get_request_phone_number(); + +$password = @$PASSWORDS[$phone_number]; + +if (!isset($password) || !$kalsms->is_validated_request($password)) +{ + header("HTTP/1.1 403 Forbidden"); + error_log("Invalid request signature"); + echo "Invalid request signature"; + return; +} + +$action = $kalsms->get_request_action(); + +switch ($action->type) +{ + case KalSMS::ACTION_INCOMING: + error_log("Received SMS from {$action->from}"); + + $reply = new KalSMS_OutgoingMessage(); + $reply->message = "You said: {$action->message}"; + + error_log("Sending reply: {$reply->message}"); + + header("Content-Type: text/xml"); + echo $action->get_response_xml(array($reply)); + return; + + case KalSMS::ACTION_OUTGOING: + $messages = array(); + + $dir = opendir($OUTGOING_DIR_NAME); + while ($file = readdir($dir)) + { + if (preg_match('#\.json$#', $file)) + { + $data = json_decode(file_get_contents("$OUTGOING_DIR_NAME/$file"), true); + if ($data && @$data['from'] == $phone_number) + { + $sms = new KalSMS_OutgoingMessage(); + $sms->id = $data['id']; + $sms->to = $data['to']; + $sms->from = $data['from']; + $sms->message = $data['message']; + $messages[] = $sms; + } + } + } + closedir($dir); + + header("Content-Type: text/xml"); + echo $action->get_response_xml($messages); + return; + + case KalSMS::ACTION_SEND_STATUS: + + $id = $action->id; + + // delete file with matching id + if (preg_match('#^\w+$#', $id) && unlink("$OUTGOING_DIR_NAME/$id.json")) + { + echo "OK"; + } + else + { + header("HTTP/1.1 404 Not Found"); + echo "invalid id"; + } + return; + + case KalSMS::ACTION_TEST: + echo "OK"; + return; + + default: + header("HTTP/1.1 404 Not Found"); + echo "Invalid action"; + return; +} \ No newline at end of file diff --git a/src/org/envaya/kalsms/App.java b/src/org/envaya/kalsms/App.java index 3d32f0f..7300214 100755 --- a/src/org/envaya/kalsms/App.java +++ b/src/org/envaya/kalsms/App.java @@ -16,8 +16,10 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.telephony.SmsManager; import android.util.Log; +import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; @@ -71,6 +73,22 @@ public class App { context.sendBroadcast(broadcast); } + public void checkOutgoingMessages() + { + String serverUrl = getServerUrl(); + if (serverUrl.length() > 0) + { + log("Checking for outgoing messages"); + new PollerTask().execute( + new BasicNameValuePair("action", App.ACTION_OUTGOING) + ); + } + else + { + log("Can't check outgoing messages; server URL not set"); + } + } + public void setOutgoingMessageAlarm() { AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); @@ -212,4 +230,20 @@ public class App { log("Sending " +sms.getLogName() + " to " + sms.getTo()); smgr.sendTextMessage(sms.getTo(), null, sms.getMessage(), sentIntent, null); } + + private class PollerTask extends HttpTask { + + public PollerTask() + { + super(app); + } + + @Override + protected void handleResponse(HttpResponse response) throws Exception { + for (OutgoingSmsMessage reply : parseResponseXML(response)) { + app.sendSMS(reply); + } + } + } + } diff --git a/src/org/envaya/kalsms/HttpTask.java b/src/org/envaya/kalsms/HttpTask.java index c9737dd..74fd4db 100755 --- a/src/org/envaya/kalsms/HttpTask.java +++ b/src/org/envaya/kalsms/HttpTask.java @@ -84,6 +84,7 @@ public class HttpTask extends AsyncTask String signature = this.getSignature(url, params); + post.setHeader("X-Kalsms-Version", "2"); post.setHeader("X-Kalsms-PhoneNumber", app.getPhoneNumber()); post.setHeader("X-Kalsms-Signature", signature); diff --git a/src/org/envaya/kalsms/IncomingMessageForwarder.java b/src/org/envaya/kalsms/IncomingMessageForwarder.java index bb336fb..27e9120 100755 --- a/src/org/envaya/kalsms/IncomingMessageForwarder.java +++ b/src/org/envaya/kalsms/IncomingMessageForwarder.java @@ -53,7 +53,6 @@ public class IncomingMessageForwarder extends BroadcastReceiver { String serverUrl = app.getServerUrl(); String message = sms.getMessageBody(); String sender = sms.getOriginatingAddress(); - String recipient = app.getPhoneNumber(); app.log("Received SMS from " + sender); @@ -64,7 +63,6 @@ public class IncomingMessageForwarder extends BroadcastReceiver { new ForwarderTask(sms).execute( new BasicNameValuePair("from", sender), - new BasicNameValuePair("to", recipient), new BasicNameValuePair("message", message), new BasicNameValuePair("action", App.ACTION_INCOMING) ); diff --git a/src/org/envaya/kalsms/Main.java b/src/org/envaya/kalsms/Main.java index b312299..686a7e5 100755 --- a/src/org/envaya/kalsms/Main.java +++ b/src/org/envaya/kalsms/Main.java @@ -133,6 +133,9 @@ public class Main extends Activity { case R.id.settings: startActivity(new Intent(this, Prefs.class)); return true; + case R.id.check_now: + app.checkOutgoingMessages(); + return true; case R.id.test: app.log("Testing server connection..."); new TestTask().execute( diff --git a/src/org/envaya/kalsms/OutgoingMessagePoller.java b/src/org/envaya/kalsms/OutgoingMessagePoller.java index 5a4c085..a8c9018 100755 --- a/src/org/envaya/kalsms/OutgoingMessagePoller.java +++ b/src/org/envaya/kalsms/OutgoingMessagePoller.java @@ -3,40 +3,14 @@ package org.envaya.kalsms; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import org.apache.http.HttpResponse; -import org.apache.http.message.BasicNameValuePair; public class OutgoingMessagePoller extends BroadcastReceiver { private App app; - private class PollerTask extends HttpTask { - - public PollerTask() - { - super(app); - } - - @Override - protected void handleResponse(HttpResponse response) throws Exception { - for (OutgoingSmsMessage reply : parseResponseXML(response)) { - app.sendSMS(reply); - } - } - } - @Override public void onReceive(Context context, Intent intent) { app = App.getInstance(context); - - String serverUrl = app.getServerUrl(); - if (serverUrl.length() > 0) - { - app.log("Checking for outgoing messages"); - new PollerTask().execute( - new BasicNameValuePair("from", app.getPhoneNumber()), - new BasicNameValuePair("action", App.ACTION_OUTGOING) - ); - } + app.checkOutgoingMessages(); } }