Skip to content

Commit 5451d60

Browse files
committed
Version 0.6.1; mainly bug fixes + heif support
1 parent aa42294 commit 5451d60

File tree

11 files changed

+89
-46
lines changed

11 files changed

+89
-46
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ The source code may be updated past the latest released version, so don't be sur
7272
If there are any issues, questions, or feature requests at all, don't hesitate to create an issue or pull request here, or email me at [email protected]. I will not run into all issues that could possibly come up, so I would really appreciate any issues you let me know about.
7373

7474
### Acknowledged issues:
75+
- Read receipts don't update until you reload a conversation. This is being worked on.
7576
- Although typing indicators do appear when the other party starts typing, they don't always disappear when they stop typing. This is also being worked on.
7677

7778
### To file an issue:

docs/Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# Changelog
22
0.6.0 → 0.6.1
3+
- Added support for heic/heif images embedded in browser
4+
- May have actually fixed duplicating text issue
5+
- Fixed issue that would prevent loss of information when database was locked (mostly manifested in missing profile pictures)
36
- Fixed issue that would offset sender names in `printListOfTexts` when `append` was false
47
- Fixed font-weight display issues
8+
- Fixed issue that would incorrectly display texts that contained both attachments and a body or subject
9+
- Fixed issue that would prevent you from typing in the subject box
10+
- Fixed issue that would prevent sender name from appearing when you received a new text in a group chat
511

612
0.5.4 → 0.6.0
713
- Made a new build script which makes it __much__ easier and faster to build SMServer from scratch

docs/Settings.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,29 @@ This is the port that the webSocket runs on; must not be the same as the server
3232
### Theme
3333
This setting sets the color theme of the web interface; Dark is the default. It is much more polished, and, I think, looks much better than the light theme, but I did make a light theme as well for easy access. As of version 0.3.8, a `Nord` theme is also available (my favorite).
3434

35-
### Toggle debug
36-
This will log basically every thing that happens, and may slow down the server considerably. Unless you're actually debugging or logging with the app, I'd highly recommend leaving this off. This does not require a server restart to take effect.
35+
### Enable subject line
36+
This will show a subject field as well as a body field in the text interface, allowing you to send texts with subject as well as with bodies. You do not need to enable this option to use subjects in the API, however.
37+
38+
### Send typing indicators
39+
When this is toggled on, typing indicators will be sent to the other party in the conversation whenever you are currently typing in the web interface.
40+
41+
### Automatically mark as read
42+
If this is toggled, whenever you view a conversation on the web interface (more specifically, whenever you use the API to request the messages to and from one conversation, and `offset` is 0), that conversation is marked as read on your device as well.
3743

3844
### Requre Authentication to view messages:
3945
Toggling this on will prevent anyone from querying the host if they have not already authenticated with the main page. I'd highly recommend leaving it on; without it, anyone can send and view texts from your device without restriction.
4046

47+
### Merge contact addresses (experimental)
48+
When this is off, messages to and from each individual address are treated as separate conversations. However, when this is toggled on, conversations are grouped by contact, as opposed to by address.
49+
50+
### Toggle debug
51+
This will log basically every thing that happens, and may slow down the server considerably. Unless you're actually debugging or logging with the app, I'd highly recommend leaving this off. This does not require a server restart to take effect.
52+
4153
### Enable backgrounding
4254
Toggling this on will prevent the server from shutting off when the app goes into the background, given that the server is already running. Even if this is on, though, the server will shut down when the app is forcibly killed from the multitasking screen. This does not require anything to take effect, simply a toggle.
4355

4456
### Enable SSL
4557
Toggling this will require you to connect to the `https://` site, instead of the `http://` site. It also encrypts everything sent to your phone for the server, preventing anyone from listening in on your messages.
4658

47-
### Mark conversations as read when viewed on web interface
48-
This should be fairly self explanatory; if this is toggled, whenever you view a conversation on the web interface, it is marked as read on your device as well.
49-
50-
### Override `No Wifi` prevention setting on main interface
59+
### Allow operation off of Wifi
5160
A user reported an issue with the server refusing to start due to thinking that the phone was not connected to wifi, so this allows you to override the section of the app that prevents you from starting the server if it is not connected to wifi.

docs/WebSocket.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ As of 0.2.0+debug2, the server supports websockets. This is, as of 0.5.0, used t
55
## Messages to client from host
66

77
### `text`
8-
Every time that the host reeives a new text, a message with the prefix `text` is sent to all connected devices. The content of this message is a JSON message with a key of `text` that describes all the necessary parameters of the most recent text that you just received. For example, if you just got sent a text that simply said 'Hello!' from the number '+11231231234', this message would look something like:
8+
Every time that the host receives a new text or sends a text through SMServer, a message with the prefix `text` is sent to all connected devices. The content of this message is a JSON message with a key of `text` that describes all the necessary parameters of the most recent text that you just received or sent. For example, if you just got sent a text that simply said 'Hello!' from the number '+11231231234', this message would look something like:
99
```json
1010
text:{ "text": { "ROWID" : "150000", "subject" : "", "chat_identifier" : "+11231231234", "balloon_bundle_id" : "", "is_from_me" : "0", "service" : "iMessage", "guid" : "3FCF9CC7-FFF3-4172-82FD-773F9A3CC89A", "text" : "Hello!", "date_read" : "0", "date" : "626981115265999744", "cache_has_attachments" : "0", "handle_id" : "100", "associated_message_type" : "0", "associated_message_guid" : ""}}
1111
```

package/deb/DEBIAN/control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: com.ianwelker.smserver
22
Depends: com.janshai.libsmserver | com.twickd.ian-welker.libsmserver
33
Name: SMServer
4-
Version: 0.6.0
4+
Version: 0.6.1
55
Architecture: iphoneos-arm
66
Description: Send texts from your browser!
77
Maintainer: Ian Welker <[email protected]>
Binary file not shown.

src/SMServer/ChatDelegate.swift

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ final class ChatDelegate {
3939
return db
4040
}
4141

42+
sqlite3_busy_timeout(db, 20)
43+
4244
self.log("opened database")
4345

4446
/// Return pointer to the database
@@ -76,14 +78,15 @@ final class ChatDelegate {
7678

7779
self.log("opened statement")
7880

81+
var main_return = [[String:String]]()
82+
7983
/// Prepare the database for querying $sqlString
8084
if sqlite3_prepare_v2(db, sqlString, -1, &statement, nil) != SQLITE_OK {
8185
let errmsg = String(cString: sqlite3_errmsg(db)!)
8286
self.log("WARNING: error preparing select: \(errmsg)", warning: true)
87+
return main_return
8388
}
8489

85-
var main_return = [[String:String]]()
86-
8790
var i = 0
8891
/// for each row, up to num_items rows
8992
while sqlite3_step(statement) == SQLITE_ROW && (i < num_items || num_items <= 0) {
@@ -220,12 +223,12 @@ final class ChatDelegate {
220223
var messages = [[String:String]]()
221224
var from_string: String = ""
222225
var fixed_num = num
223-
226+
224227
if num.contains(",") {
225228
/// So that you can merge multiple conversations
226229
fixed_num = num.split(separator: ",").joined(separator: "\" or chat_identifier is \"")
227230
}
228-
231+
229232
if from != 0 {
230233
from_string = " and \(is_group ? "m." : "")is_from_me is \(from == 1 ? 1 : 0)"
231234
}
@@ -257,7 +260,7 @@ final class ChatDelegate {
257260
let name = getDisplayNameWithDb(sms_db: db, contact_db: contact_db, chat_id: messages[i]["id"] ?? "")
258261
messages[i]["sender"] = name
259262
}
260-
263+
261264
if messages[i]["payload_data"]?.count ?? 0 > 0 && messages[i]["ROWID"] != nil {
262265
messages[i]["balloon_bundle_id"] = "com.apple.messages.URLBalloonProvider"
263266
let link_info = getLinkInfo(messages[i]["ROWID"]!, db: db)
@@ -395,7 +398,6 @@ final class ChatDelegate {
395398
}
396399

397400
new_chat["chat_identifier"] = i["c.chat_identifier"]
398-
new_chat["send_to"] = i["c.chat_identifier"]
399401
new_chat["time_marker"] = i["m.date"]
400402
new_chat["pinned"] = "false"
401403

@@ -482,7 +484,6 @@ final class ChatDelegate {
482484

483485
/// Use default profile if you don't have them in your contacts
484486
if docid.count == 0 {
485-
486487
let image_dat = UIImage(named: "profile")
487488
let pngdata = (image_dat?.pngData())!
488489

@@ -500,6 +501,11 @@ final class ChatDelegate {
500501
if sqlite3_prepare_v2(image_db, sqlString, -1, &statement, nil) != SQLITE_OK {
501502
let errmsg = String(cString: sqlite3_errmsg(image_db)!)
502503
self.log("WARNING: error preparing select: \(errmsg)", warning: true)
504+
505+
let image_dat = UIImage(named: "profile")
506+
let pngdata = (image_dat?.pngData())!
507+
508+
return pngdata
503509
}
504510

505511
var pngdata: Data
@@ -597,9 +603,14 @@ final class ChatDelegate {
597603
let parsed_path = path.replacingOccurrences(of: "../", with: "") /// To prevent LFI
598604

599605
do {
600-
/// Pretty strsightforward -- get data and return it
601-
let attachment_data = try Data.init(contentsOf: URL(fileURLWithPath: ChatDelegate.imageStoragePrefix + parsed_path))
602-
return attachment_data
606+
/// Pretty straightforward -- get data and return it
607+
if (getAttachmentType(path: parsed_path) == "image/heic") { /// Since they're used so much
608+
let attachment_data = UIImage(contentsOfFile: ChatDelegate.imageStoragePrefix + parsed_path)
609+
return attachment_data?.jpegData(compressionQuality: 0) ?? Data.init(capacity: 0)
610+
} else {
611+
let attachment_data = try Data.init(contentsOf: URL(fileURLWithPath: ChatDelegate.imageStoragePrefix + parsed_path))
612+
return attachment_data
613+
}
603614
} catch {
604615
self.log("WARNING: failed to load image for path \(ChatDelegate.imageStoragePrefix + path)", warning: true)
605616
return Data.init(capacity: 0)
@@ -805,7 +816,24 @@ final class ChatDelegate {
805816

806817
if db == nil || contact_db == nil { return [String:String]() }
807818

808-
var text = selectFromSql(db: db, columns: ["m.ROWID", "m.guid", "m.text", "m.subject", "m.is_from_me", "m.date", "m.service", "m.cache_has_attachments", "m.handle_id", "m.balloon_bundle_id", "m.associated_message_guid", "m.associated_message_type", "h.id", "c.chat_identifier", "c.room_name"], table: "message m", condition: "left join handle h on h.ROWID = m.handle_id inner join chat_message_join j on j.message_id = m.ROWID inner join chat c on j.chat_id = c.ROWID where m.guid is \"\(guid)\"", num_items: 1, offset: 0, split_ids: true)[0]
819+
var text_array = selectFromSql(db: db, columns: ["m.ROWID", "m.guid", "m.text", "m.subject", "m.is_from_me", "m.date", "m.service", "m.cache_has_attachments", "m.handle_id", "m.balloon_bundle_id", "m.associated_message_guid", "m.associated_message_type", "h.id", "c.chat_identifier", "c.room_name"], table: "message m", condition: "left join handle h on h.ROWID = m.handle_id inner join chat_message_join j on j.message_id = m.ROWID inner join chat c on j.chat_id = c.ROWID where m.guid is \"\(guid)\"", num_items: 1, offset: 0, split_ids: true)
820+
821+
var total = 0;
822+
let begin = 50000;
823+
let max = begin * 5
824+
825+
while text_array.count == 0 && total <= max {
826+
usleep(useconds_t(begin)) /// I hate race conditions so much but I have no idea how to avoid it for this
827+
total += begin
828+
829+
text_array = selectFromSql(db: db, columns: ["m.ROWID", "m.guid", "m.text", "m.subject", "m.is_from_me", "m.date", "m.service", "m.cache_has_attachments", "m.handle_id", "m.balloon_bundle_id", "m.associated_message_guid", "m.associated_message_type", "h.id", "c.chat_identifier", "c.room_name"], table: "message m", condition: "left join handle h on h.ROWID = m.handle_id inner join chat_message_join j on j.message_id = m.ROWID inner join chat c on j.chat_id = c.ROWID where m.guid is \"\(guid)\"", num_items: 1, offset: 0, split_ids: true)
830+
}
831+
832+
guard text_array.count > 0 else {
833+
return [String:String]()
834+
}
835+
836+
var text = text_array[0]
809837

810838
if text["cache_has_attachments"] == "1" && text["ROWID"] != nil {
811839
let a = getAttachmentFromMessage(mid: text["ROWID"] ?? "") /// get list of attachments
@@ -820,7 +848,7 @@ final class ChatDelegate {
820848
}
821849

822850
if text["room_name"]?.count ?? 0 > 0 && text["is_from_me"] == "0" {
823-
let name = getDisplayNameWithDb(sms_db: db, contact_db: contact_db, chat_id: text["handle_id"] ?? "")
851+
let name = getDisplayNameWithDb(sms_db: db, contact_db: contact_db, chat_id: text["id"] ?? "")
824852
text["sender"] = name
825853
}
826854

src/SMServer/ContentView.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct ContentView: View {
2323
@State var secure: Bool = UserDefaults.standard.object(forKey: "is_secure") as? Bool ?? true
2424
@State var override_no_wifi: Bool = UserDefaults.standard.object(forKey: "override_no_wifi") as? Bool ?? false
2525
@State var background: Bool = UserDefaults.standard.object(forKey: "enable_backgrounding") as? Bool ?? true
26-
26+
2727
@State var mark_when_read: Bool = true
2828
@State var view_settings: Bool = false
2929
@State var server_running: Bool = false
@@ -106,7 +106,7 @@ struct ContentView: View {
106106
res.send(self.checkIfAuthenticated(ras: ip) ? self.main_page : self.gatekeeper_page)
107107
}
108108

109-
server.add("/requests") { (req, res, next) in
109+
server.add("/requests") { (req, res, _) in
110110
/// There is no authentication handler here 'cause it handles that within parseAndReturn()
111111
/// since they send the password auth request to this subdirectory
112112
/// This handler is part of the API, and returns JSON info.
@@ -330,7 +330,7 @@ struct ContentView: View {
330330
let default_num_messages = UserDefaults.standard.object(forKey: "num_messages") as? Int ?? 100
331331
let default_num_chats = UserDefaults.standard.object(forKey: "num_chats") as? Int ?? 40
332332
let default_num_photos = UserDefaults.standard.object(forKey: "num_photos") as? Int ?? 40
333-
333+
334334
let subjects_enabled = UserDefaults.standard.object(forKey: "subjects_enabled") as? Bool ?? false
335335
let light_theme = UserDefaults.standard.object(forKey: "light_theme") as? Bool ?? false
336336
let nord_theme = UserDefaults.standard.object(forKey: "nord_theme") as? Bool ?? false
@@ -375,6 +375,8 @@ struct ContentView: View {
375375

376376
func setNewestTexts(_ guid: String) {
377377
/// Is called when you receive a new text; Tells the socket to send a notification to all connected that you received a new text
378+
guard server.isListening && socket.server?.webSockets.count ?? 0 > 0 else { return }
379+
378380
let text = ContentView.chat_delegate.getTextByGUID(guid);
379381
let json = encodeToJson(object: text, title: "text")
380382

@@ -609,7 +611,6 @@ struct ContentView: View {
609611
HStack {
610612
Button(action: {
611613
self.loadFiles()
612-
//ContentView.sender.launchMobileSMS()
613614
ContentView.chat_delegate.refreshVars()
614615
self.socket.refreshVars()
615616
}) {

src/SMServer/html/chats.html

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@
4444
prefix = "http://192.168.0.180:8741";
4545
num_texts_to_load = 100;
4646
num_chats_to_load = 60;
47+
num_photos_to_load = 40;
4748
socket_port = 8740;
4849
socket_address = prefix.split("/")[2].split(":")[0];
50+
debug = true;
51+
subject = false;
4952
}
5053

5154
function timeConverter(t, ts_only = false, apple = true, rev = false) {
@@ -400,6 +403,8 @@
400403
var ft = fts.length === undefined ? [fts] : fts;
401404
if (ft.length === 0) continue;
402405

406+
if (!append) ft.reverse();
407+
403408
var ld = i > 0 ? texts[i-1].date : 0;
404409
var notLast = false;
405410
var n = append ? i+1 : i-1;
@@ -424,6 +429,8 @@
424429
et[et.length-1].classList.add("notLast");
425430
else if (texts[i].date - et[et.length-1].getAttribute("date") >= 3600000000000)
426431
append_time_display = true;
432+
else if (is_group && !sameSender)
433+
sender_box.innerText = texts[i].sender;
427434
} else {
428435
var hourTillFirst = texts[i].date - et[0].getAttribute("date") >= 3600000000000;
429436
if (!hourTillFirst) document.getElementsByClassName("time-display")[0].outerHTML = "";
@@ -452,7 +459,7 @@
452459

453460
for (var j = 0; j < ft.length; j++) {
454461
if (ft[j].innerHTML === undefined || ft[j].innerHTML === "") continue;
455-
if (notLast || j < ft.length - 1) ft[j].classList.add("notLast");
462+
if (notLast || (append && j < ft.length - 1) || (!append && j > 0)) ft[j].classList.add("notLast");
456463
if (append) tc.appendChild(ft[j]);
457464
else tc.insertBefore(ft[j], document.getElementById("moretextsbutton").nextSibling);
458465
}
@@ -505,7 +512,7 @@
505512
var lt = undefined;
506513

507514
for (var i = et.length - 1; i >= 0 && lt === undefined; i--)
508-
if (et[i].className.includes("from_me") && et[i].className.includes("iMessage") && et[i].getAttribute("date_read") !== "0")
515+
if (et[i].className.includes("from_me") && et[i].className.includes("iMessage") && et[i].getAttribute("date_read") !== "0" && et[i].getAttribute("date_read") !== "undefined")
509516
lt = et[i];
510517

511518
if (lt !== undefined) {
@@ -736,17 +743,6 @@
736743
photos.value = "";
737744

738745
autoGrow();
739-
740-
var seg = 6000; /// kinda arbitrary
741-
var at_sleep = 400;
742-
var sleep_time = 250;
743-
if (has_attachments)
744-
await sleep(Math.max((total_size / seg), at_sleep));
745-
else
746-
await sleep(sleep_time);
747-
748-
getLatestText(1, current_chat_id, true);
749-
setChatAsTop(current_chat_id, oldval);
750746
}
751747
}
752748

@@ -875,14 +871,16 @@
875871

876872
var name = await getTextFromURL(`${prefix}/requests?name=${chat}`);
877873

878-
if (notif_text.text.replace(//g, "").trim() === "" && notif_text.cache_has_attachments === "1")
879-
new_text = `Attachment: ${notif_text.attachment_type.split(":")[0]}`
874+
if (notif_text.is_from_me === "0") {
875+
if (notif_text.text.replace(//g, "").trim() === "" && notif_text.cache_has_attachments === "1")
876+
new_text = `Attachment: ${notif_text.attachment_type.split(":")[0]}`
880877

881-
if (notif_text.sender !== undefined && notif_text.sender !== "nil")
882-
new_text = `${notif_text.sender}: ${new_text}`;
878+
if (notif_text.sender !== undefined && notif_text.sender !== "nil")
879+
new_text = `${notif_text.sender}: ${new_text}`;
883880

884-
if (window.Notification && Notification.permission === "granted" && (!document.hasFocus() || chat !== current_chat_id))
885-
var n = new Notification(name, { body: new_text, icon: "/favicon.ico"});
881+
if (window.Notification && Notification.permission === "granted" && (!document.hasFocus() || chat !== current_chat_id))
882+
var n = new Notification(name, { body: new_text, icon: "/favicon.ico"});
883+
}
886884

887885
if (chat.length !== 0) {
888886
if (btn === undefined || btn === null || chat === "any") {
@@ -1159,7 +1157,7 @@
11591157
sbox.id = "subjectBox";
11601158
sbox.setAttribute("name", "subject");
11611159
sbox.setAttribute("oninput", "if (event.keyCode !== 13) inputKeyDown(true);");
1162-
sbox.setAttribute("onkeydown", "if (event.keyCode === 13) document.getElementById('sendbox').focus(); event.preventDefault();");
1160+
sbox.setAttribute("onkeydown", "if (event.keyCode === 13) { document.getElementById('sendbox').focus(); event.preventDefault(); }");
11631161
sbox.setAttribute("form", tbox.getAttribute("form"));
11641162
sbox.setAttribute("placeholder", "Subject");
11651163

src/SMServer/html/light_theme.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
:root {
22
--text-box-gradient: linear-gradient(to bottom right, #a6a6a6, #989898);
33
--body-background: #fff;
4-
--font-color: #111;
4+
--font-color: #353535;
55
--main-background: #f0f0f0;
66
--chats-border: #ccc;
77
--light-button-background: #ccc;

0 commit comments

Comments
 (0)