diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessage.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessage.java index 7dee2a8..9a21eda 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessage.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessage.java @@ -9,7 +9,8 @@ import java.util.TimeZone; public class CMessage { - public final long Timestamp ; + public final long SCN_ID; + public final long Timestamp; public final String Title; public final String Content; public final PriorityEnum Priority; @@ -21,8 +22,9 @@ public class CMessage _format.setTimeZone(TimeZone.getDefault()); } - public CMessage(long t, String mt, String mc, PriorityEnum p) + public CMessage(long id, long t, String mt, String mc, PriorityEnum p) { + SCN_ID = id; Timestamp = t; Title = mt; Content = mc; diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java index 3614148..9579730 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java @@ -38,14 +38,15 @@ public class CMessageList String title = sharedPref.getString("message["+i+"].title", ""); String content = sharedPref.getString("message["+i+"].content", ""); PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1)); + long scnid = sharedPref.getLong("message["+i+"].scnid", 0); - Messages.add(new CMessage(time, title, content, prio)); + Messages.add(new CMessage(scnid, time, title, content, prio)); } } - public CMessage add(final long time, final String title, final String content, final PriorityEnum pe) + public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe) { - CMessage msg = new CMessage(time, title, content, pe); + CMessage msg = new CMessage(scnid, time, title, content, pe); boolean run = SCNApp.runOnUiThread(() -> { @@ -58,11 +59,12 @@ public class CMessageList while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0); - e.putInt("message_count", count+1); - e.putLong("message["+count+"].timestamp", time); - e.putString("message["+count+"].title", title); - e.putString("message["+count+"].content", content); - e.putInt("message["+count+"].priority", pe.ID); + e.putInt( "message_count", count+1); + e.putLong( "message["+count+"].timestamp", time); + e.putString("message["+count+"].title", title); + e.putString("message["+count+"].content", content); + e.putInt( "message["+count+"].priority", pe.ID); + e.putLong( "message["+count+"].scnid", scnid); e.apply(); @@ -77,7 +79,7 @@ public class CMessageList if (!run) { - Messages.add(new CMessage(time, title, content, pe)); + Messages.add(new CMessage(scnid, time, title, content, pe)); fullSave(); } @@ -109,10 +111,11 @@ public class CMessageList for (int i = 0; i < Messages.size(); i++) { - e.putLong("message["+i+"].timestamp", Messages.get(i).Timestamp); - e.putString("message["+i+"].title", Messages.get(i).Title); - e.putString("message["+i+"].content", Messages.get(i).Content); - e.putInt("message["+i+"].priority", Messages.get(i).Priority.ID); + e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp); + e.putString("message["+i+"].title", Messages.get(i).Title); + e.putString("message["+i+"].content", Messages.get(i).Content); + e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID); + e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID); } e.apply(); diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java index 3d51ccf..1ee5702 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java @@ -41,7 +41,7 @@ public class ServerCommunication @Override public void onFailure(Call call, IOException e) { - e.printStackTrace(); + Log.e("SC:register", e.toString()); SCNApp.showToast("Communication with server failed", 4000); SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); }); } @@ -77,7 +77,7 @@ public class ServerCommunication } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:register", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } finally @@ -89,7 +89,7 @@ public class ServerCommunication } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:register", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } } @@ -107,7 +107,7 @@ public class ServerCommunication @Override public void onFailure(Call call, IOException e) { - e.printStackTrace(); + Log.e("SC:update_1", e.toString()); SCNApp.showToast("Communication with server failed", 4000); SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); }); } @@ -143,7 +143,7 @@ public class ServerCommunication } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:update_1", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } finally @@ -155,7 +155,7 @@ public class ServerCommunication } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:update_1", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } } @@ -171,7 +171,7 @@ public class ServerCommunication client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { - e.printStackTrace(); + Log.e("SC:update_2", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } @@ -201,7 +201,7 @@ public class ServerCommunication SCNApp.refreshAccountTab(); } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:update_2", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } finally { SCNApp.runOnUiThread(() -> { @@ -213,7 +213,7 @@ public class ServerCommunication } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:update_2", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } } @@ -229,7 +229,7 @@ public class ServerCommunication client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { - e.printStackTrace(); + Log.e("SC:info", e.toString()); SCNApp.showToast("Communication with server failed", 4000); SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); @@ -281,7 +281,7 @@ public class ServerCommunication SCNApp.refreshAccountTab(); } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:info", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } finally { SCNApp.runOnUiThread(() -> { @@ -293,7 +293,7 @@ public class ServerCommunication } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:info", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } } @@ -311,7 +311,7 @@ public class ServerCommunication client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { - e.printStackTrace(); + Log.e("SC:upgrade", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } @@ -341,7 +341,7 @@ public class ServerCommunication SCNApp.refreshAccountTab(); } catch (Exception e) { - e.printStackTrace(); + Log.e("SC:upgrade", e.toString()); SCNApp.showToast("Communication with server failed", 4000); } finally { SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); }); @@ -356,6 +356,32 @@ public class ServerCommunication } } + public static void ack(int id, String key, CMessage msg) + { + try + { + Request request = new Request.Builder() + .url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg.SCN_ID) + .build(); + + client.newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + Log.e("SC:ack", e.toString()); + } + + @Override + public void onResponse(Call call, Response response) { + // ???? + } + }); + } + catch (Exception e) + { + Log.e("SC:ack", e.toString()); + } + } + private static boolean json_bool(JSONObject o, String key) throws JSONException { Object v = o.get(key); @@ -375,5 +401,4 @@ public class ServerCommunication { return o.getString(key); } - } diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java index d5150a2..a230bf5 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java @@ -8,6 +8,7 @@ import com.blackforestbytes.simplecloudnotifier.model.CMessage; import com.blackforestbytes.simplecloudnotifier.model.CMessageList; import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; +import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; @@ -36,9 +37,9 @@ public class FBMService extends FirebaseMessagingService String title = remoteMessage.getData().get("title"); String content = remoteMessage.getData().get("body"); PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority")); + long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id")); - CMessage msg = CMessageList.inst().add(time, title, content, prio); - + CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio); if (SCNApp.isBackground()) { @@ -48,6 +49,8 @@ public class FBMService extends FirebaseMessagingService { NotificationService.inst().showForeground(msg); } + + ServerCommunication.ack(msg); } catch (Exception e) { diff --git a/web/ack.php b/web/ack.php new file mode 100644 index 0000000..39ce81d --- /dev/null +++ b/web/ack.php @@ -0,0 +1,47 @@ + false, 'errid'=>101, 'message' => 'Missing parameter [[user_id]]'])); +if (!isset($INPUT['user_key'])) die(json_encode(['success' => false, 'errid'=>102, 'message' => 'Missing parameter [[user_key]]'])); +if (!isset($INPUT['scn_msg_id'])) die(json_encode(['success' => false, 'errid'=>103, 'message' => 'Missing parameter [[scn_msg_id]]'])); + +$user_id = $INPUT['user_id']; +$user_key = $INPUT['user_key']; +$scn_msg_id = $INPUT['scn_msg_id']; + +//---------------------- + +$pdo = getDatabase(); + +$stmt = $pdo->prepare('SELECT user_id, user_key, quota_today, is_pro, quota_day, fcm_token FROM users WHERE user_id = :uid LIMIT 1'); +$stmt->execute(['uid' => $user_id]); +$datas = $stmt->fetchAll(PDO::FETCH_ASSOC); + +if (count($datas)<=0) die(json_encode(['success' => false, 'errid'=>201, 'message' => 'User not found'])); +$data = $datas[0]; + +if ($data === null) die(json_encode(['success' => false, 'errid'=>202, 'message' => 'User not found'])); +if ($data['user_id'] !== (int)$user_id) die(json_encode(['success' => false, 'errid'=>203, 'message' => 'UserID not found'])); +if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid'=>204, 'message' => 'Authentification failed'])); + +$stmt = $pdo->prepare('SELECT ack FROM messages WHERE scn_message_id=:smid AND sender_user_id=:uid LIMIT 1'); +$stmt->execute(['smid' => $scn_msg_id, 'uid' => $user_id]); +$datas = $stmt->fetchAll(PDO::FETCH_ASSOC); + +if (count($datas)<=0) die(json_encode(['success' => false, 'errid'=>301, 'message' => 'Message not found'])); + +$stmt = $pdo->prepare('UPDATE messages SET ack=1 WHERE scn_message_id=:smid AND sender_user_id=:uid'); +$stmt->execute(['smid' => $scn_msg_id, 'uid' => $user_id]); + +echo json_encode( +[ + 'success' => true, + 'prev_ack' => $datas[0]['ack'], + 'new_ack' => 1, + 'message' => 'ok' +]); +return 0; \ No newline at end of file diff --git a/web/schema.sql b/web/schema.sql index 634d604..1a3b2f8 100644 --- a/web/schema.sql +++ b/web/schema.sql @@ -24,12 +24,13 @@ CREATE TABLE `messages` `sender_user_id` INT(11) NOT NULL, `timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ack` BIT NOT NULL DEFAULT 0, `title` VARCHAR(256) NOT NULL, `content` VARCHAR(12288) NULL, `priority` INT(11) NOT NULL, - `fcn_message_id` VARCHAR(256) NULL, + `fcm_message_id` VARCHAR(256) NULL, `usr_message_id` VARCHAR(256) NULL, PRIMARY KEY (`scn_message_id`) diff --git a/web/send.php b/web/send.php index f5be965..6c8f906 100644 --- a/web/send.php +++ b/web/send.php @@ -29,9 +29,10 @@ try if ($priority !== '0' && $priority !== '1' && $priority !== '2') api_return(400, json_encode(['success' => false, 'error' => 1104, 'errhighlight' => 105, 'message' => 'Invalid priority'])); - if (strlen(trim($message)) == 0) api_return(400, json_encode(['success' => false, 'error' => 1201, 'errhighlight' => 103, 'message' => 'No title specified'])); - if (strlen($message) > 120) api_return(400, json_encode(['success' => false, 'error' => 1202, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)'])); - if (strlen($content) > 10000) api_return(400, json_encode(['success' => false, 'error' => 1203, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)'])); + if (strlen(trim($message)) == 0) api_return(400, json_encode(['success' => false, 'error' => 1201, 'errhighlight' => 103, 'message' => 'No title specified'])); + if (strlen($message) > 120) api_return(400, json_encode(['success' => false, 'error' => 1202, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)'])); + if (strlen($content) > 10000) api_return(400, json_encode(['success' => false, 'error' => 1203, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)'])); + if ($usrmsgid != null && strlen($usrmsgid) > 64) api_return(400, json_encode(['success' => false, 'error' => 1204, 'errhighlight' => -1, 'message' => 'MessageID too long (64 characters)'])); //------------------------------------------------------------------ @@ -84,6 +85,22 @@ try //------------------------------------------------------------------ + $pdo->beginTransaction(); + + + $stmt = $pdo->prepare('INSERT INTO messages (sender_user_id, title, content, priority, fcm_message_id, usr_message_id) VALUES (:suid, :t, :c, :p, :fmid, :umid)'); + $stmt->execute( + [ + 'suid' => $user_id, + 't' => $message, + 'c' => $content, + 'p' => $priority, + 'fmid' => null, + 'umid' => $usrmsgid, + ]); + + $scn_msg_id = $pdo->lastInsertId(); + $url = "https://fcm.googleapis.com/fcm/send"; $payload = json_encode( [ @@ -102,6 +119,7 @@ try 'priority' => $priority, 'timestamp' => time(), 'usr_msg_id' => $usrmsgid, + 'scn_msg_id' => $scn_msg_id, ] ]); $header= @@ -117,28 +135,24 @@ try if (try_json($httpresult, ['success']) != 1) { reportError("FCM communication failed (success_1 <> true)\n\n".$httpresult); + $pdo->rollBack(); api_return(500, json_encode(['success' => false, 'error' => 9902, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.'])); } } catch (Exception $e) { reportError("FCM communication failed", $e); + $pdo->rollBack(); api_return(500, json_encode(['success' => false, 'error' => 9901, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.'."\n\n".'Exception: ' . $e->getMessage()])); } $stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), messages_sent=messages_sent+1, quota_today=:q, quota_day=NOW() WHERE user_id = :uid'); $stmt->execute(['uid' => $user_id, 'q' => $new_quota]); - $stmt = $pdo->prepare('INSERT INTO messages (sender_user_id, title, content, priority, fcn_message_id, usr_message_id) VALUES (:suid, :t, :c, :p, :fmid, :umid)'); - $stmt->execute( - [ - 'suid' => $user_id, - 't' => $message, - 'c' => $content, - 'p' => $priority, - 'fmid' => try_json($httpresult, ['results', 0, 'message_id']), - 'umid' => $usrmsgid, - ]); + $stmt = $pdo->prepare('UPDATE messages SET fcm_message_id=:fmid WHERE scn_message_id=:smid'); + $stmt->execute([ 'fmid' => try_json($httpresult, ['results', 0, 'message_id']), 'smid' => $scn_msg_id ]); + + $pdo->commit(); api_return(200, json_encode( [ @@ -150,10 +164,12 @@ try 'quota' => $new_quota, 'is_pro' => $data['is_pro'], 'quota_max' => Statics::quota_max($data['is_pro']), + 'scn_msg_id' => $scn_msg_id, ])); } catch (Exception $mex) { reportError("Root try-catch triggered", $mex); + if ($pdo->inTransaction()) $pdo->rollBack(); api_return(500, json_encode(['success' => false, 'error' => 9903, 'errhighlight' => -1, 'message' => 'PHP script threw exception.'."\n\n".'Exception: ' . $e->getMessage()])); }