-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathINSTALL
executable file
·338 lines (278 loc) · 16.5 KB
/
INSTALL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
#!/usr/bin/env bash
function main {
# Prompt for info
read -p $'\033[1;37mUsername to ssh into on Apple machine:\033[0m\n' USERNAME
read -p $'\033[1;37mIP address of Apple machine:\033[0m\n' IPADDR
# Make this input later.
DBNAME="./localCode/sms.db"
CREATED_LOCAL_DB=0
INSTALLED_SCRIPTS=0
UPDATED_CHATDB=0
CREATED_MSGQUEUEDB=0
CREATED_REMOTE_CONFIG=0
CREATED_LOCAL_SECRETS=0
test_ssh
create_database
get_homedir_expansion
find_message_db
install_scripts
create_certs
update_macbook_chatdb
create_incoming_message_db
create_remote_config
create_secrets_json
}
function create_secrets_json {
begin "Creating secrets.json on local machine..."
cat << END_DOCUMENT > localCode/secrets.json
{
"user": "${USERNAME}",
"ip": "${IPADDR}",
"scriptPath": "${HOMEDIR_EXPANSION}/Applications/node-server/addOutgoing.py",
"retrieveScriptPath": "${HOMEDIR_EXPANSION}/Applications/node-server/getMessages.py",
"serverCrt": "./server.crt",
"clientCrt": "./client.crt",
"clientKey": "./client.key"
}
END_DOCUMENT
pass "Created secrets.json at $(pwd)/localCode/secrets.json"
CREATED_LOCAL_SECRETS=1
}
function create_remote_config {
begin "Creating config.json on Apple machine..."
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
cat << END_CAT > "${HOMEDIR_EXPANSION}"/Applications/node-server/.env
QUEUE_PATH=${HOMEDIR_EXPANSION}/Library/Messages/msgqueue.db
CHAT_PATH=${HOMEDIR_EXPANSION}/Library/Messages/chat.db
END_CAT
exit \$?
END_DOCUMENT
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Could not create .env at ${HOMEDIR_EXPANSION}/Applications/node-server/.env"
else
pass "Created config.json at ${HOMEDIR_EXPANSION}/Applications/node-server/.env"
CREATED_REMOTE_CONFIG=1
fi
}
function create_incoming_message_db {
begin "Creating msgqueue.db on Apple machine..."
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
sqlite3 "${HOMEDIR_EXPANSION}"/Library/Messages/msgqueue.db << END_SQL
CREATE TABLE IF NOT EXISTS message ( ROWID INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER, text TEXT );
CREATE TABLE IF NOT EXISTS reaction ( ROWID INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER, associated_guid TEXT, associated_type INTEGER );
CREATE TABLE IF NOT EXISTS rename ( ROWID INTEGER PRIMARY KEY AUTOINCREMENT, chat_id INTEGER, group_title TEXT );
CREATE TABLE IF NOT EXISTS chat ( ROWID INTEGER PRIMARY KEY AUTOINCREMENT, recipient_string TEXT, text TEXT );
END_SQL
exit \$?
END_DOCUMENT
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Could not create msgqueue.db on Apple machine."
else
pass "Created msgqueue.db at ${HOMEDIR_EXPANSION}/Library/Messages/msgqueue.db"
CREATED_MSGQUEUEDB=1
fi
}
function update_macbook_chatdb {
begin "Updating chat.db for ${USERNAME} on Apple machine..."
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
sqlite3 "${HOMEDIR_EXPANSION}"/Library/Messages/chat.db << END_SQL
CREATE TABLE IF NOT EXISTS message_update_date_join (message_id INTEGER REFERENCES message (ROWID) ON DELETE CASCADE, message_update_date INTEGER DEFAULT 0, PRIMARY KEY (message_id, message_update_date));
CREATE TRIGGER IF NOT EXISTS insert_last_update_date AFTER INSERT ON message BEGIN INSERT INTO message_update_date_join (message_id, message_update_date) VALUES ( NEW.ROWID, strftime('%s','now') ); END;
CREATE TRIGGER IF NOT EXISTS update_last_update_date AFTER UPDATE ON message BEGIN UPDATE message_update_date_join SET message_update_date = strftime('%s','now') WHERE message_id = OLD.ROWID; END;
END_SQL
exit \$?
END_DOCUMENT
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Could not update chat.db at ${CHATDB_LOCATION}"
else
pass "Updated chat.db at ${CHATDB_LOCATION}"
UPDATED_CHATDB=1
fi
}
function create_certs {
begin "Creating certificates for https..."
# Macs typically use libreSSL instead of openSSL. Older versions lack the -addext option for the req command.
# See https://github.com/libressl-portable/portable/issues/544
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
cd "${INSTALL_LOCATION}"
openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt -subj '/CN=localhost' -extensions SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=\"DNS:localhost, IP:${IPADDR}\""))
openssl req -newkey rsa:2048 -keyout client.key -out client.csr -nodes -subj "/CN=iMessageForwarderClient"
openssl x509 -req -in client.csr -CA server.crt -CAkey server.key -out client.crt -set_serial 01 -days 365
rm client.csr
END_DOCUMENT
FILES=( 'server.crt' 'client.crt' 'client.key' )
for FILE in "${FILES[@]}"
do
OUTPUT=$(q=1 scp_and_check "${USERNAME}"@"${IPADDR}":"${INSTALL_LOCATION}"/"${FILE}" ./localCode/"${FILE}")
if [ $OUTPUT -ne 0 ]
then
FAIL_FLAG=1
fi
done
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
cd "${INSTALL_LOCATION}"
rm client.key client.crt
END_DOCUMENT
}
function install_scripts {
begin "Installing python scripts on Apple machine..."
INSTALL_LOCATION="${HOMEDIR_EXPANSION}/Applications/node-server"
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
mkdir "${INSTALL_LOCATION}"
END_DOCUMENT
SOURCE_LOCATION="$(pwd)/remoteCode/node-server"
FAIL_FLAG=0
FILES=( 'autoMessage.py' 'getMessages.py' 'index.js' 'package.json' 'package-lock.json' 'testDb.db' )
for FILE in "${FILES[@]}"
do
OUTPUT=$(q=1 scp_and_check "${SOURCE_LOCATION}"/"${FILE}" "${USERNAME}"@"${IPADDR}":"${INSTALL_LOCATION}")
if [ $OUTPUT -ne 0 ]
then
FAIL_FLAG=1
fi
done
OUTPUT=$(q=1 r=1 scp_and_check "${SOURCE_LOCATION}"/darkmode "${USERNAME}"@"${IPADDR}":"${INSTALL_LOCATION}")
if [ $OUTPUT -ne 0 ]
then
FAIL_FLAG=1
fi
ssh "${USERNAME}"@"${IPADDR}" << END_DOCUMENT
/bin/bash << END_BASH
cd "${INSTALL_LOCATION}"
npm install
END_BASH
END_DOCUMENT
if [ "$FAIL_FLAG" -eq "0" ]
then
pass "Transferred remote code to macbook."
INSTALLED_SCRIPTS=1
fi
}
function find_message_db {
begin "Finding chat.db on Apple machine..."
ssh "${USERNAME}"@"${IPADDR}" /bin/bash << END_DOCUMENT
test -f ${HOMEDIR_EXPANSION}/Library/Messages/chat.db
exit \$?
END_DOCUMENT
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Could not find message database."
else
CHATDB_LOCATION="${HOMEDIR_EXPANSION}/Library/Messages/chat.db"
pass "Found chat.db at ${CHATDB_LOCATION}."
fi
}
function get_homedir_expansion {
begin "Finding the home directory for ${USERNAME} on Apple machine..."
HOMEDIR_EXPANSION=$(ssh "${USERNAME}"@"${IPADDR}" echo '$HOME';)
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Could not find the home directory for ${USERNAME} on Apple machine..."
else
pass "Found home directory at ${HOMEDIR_EXPANSION}"
fi
}
function create_database {
begin "Creating a database at ${DBNAME} for incoming messages on local machine..."
sqlite3 ./localCode/sms.db << END_DOCUMENT
CREATE TABLE IF NOT EXISTS _SqliteDatabaseProperties (key TEXT, value TEXT, UNIQUE(key));
CREATE TABLE IF NOT EXISTS handle (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, id TEXT NOT NULL, country TEXT, service TEXT NOT NULL, uncanonicalized_id TEXT, UNIQUE (id, service) );
CREATE TABLE IF NOT EXISTS deleted_messages (ROWID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, guid TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS chat_message_join (chat_id INTEGER REFERENCES chat (ROWID) ON DELETE CASCADE, message_id INTEGER REFERENCES message (ROWID) ON DELETE CASCADE, PRIMARY KEY (chat_id, message_id));
CREATE TABLE IF NOT EXISTS chat_handle_join (chat_id INTEGER REFERENCES chat (ROWID) ON DELETE CASCADE, handle_id INTEGER REFERENCES handle (ROWID) ON DELETE CASCADE, UNIQUE(chat_id, handle_id));
CREATE TABLE IF NOT EXISTS message (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, guid TEXT UNIQUE NOT NULL, text TEXT, replace INTEGER DEFAULT 0, service_center TEXT, handle_id INTEGER DEFAULT 0, subject TEXT, country TEXT, attributedBody BLOB, version INTEGER DEFAULT 0, type INTEGER DEFAULT 0, service TEXT, account TEXT, account_guid TEXT, error INTEGER DEFAULT 0, date INTEGER, date_read INTEGER, date_delivered INTEGER, is_delivered INTEGER DEFAULT 0, is_finished INTEGER DEFAULT 0, is_emote INTEGER DEFAULT 0, is_from_me INTEGER DEFAULT 0, is_empty INTEGER DEFAULT 0, is_delayed INTEGER DEFAULT 0, is_auto_reply INTEGER DEFAULT 0, is_prepared INTEGER DEFAULT 0, is_read INTEGER DEFAULT 0, is_system_message INTEGER DEFAULT 0, is_sent INTEGER DEFAULT 0, has_dd_results INTEGER DEFAULT 0, is_service_message INTEGER DEFAULT 0, is_forward INTEGER DEFAULT 0, was_downgraded INTEGER DEFAULT 0, is_archive INTEGER DEFAULT 0, cache_has_attachments INTEGER DEFAULT 0, cache_roomnames TEXT, was_data_detected INTEGER DEFAULT 0, was_deduplicated INTEGER DEFAULT 0, is_audio_message INTEGER DEFAULT 0, is_played INTEGER DEFAULT 0, date_played INTEGER, item_type INTEGER DEFAULT 0, other_handle INTEGER DEFAULT 0, group_title TEXT, group_action_type INTEGER DEFAULT 0, share_status INTEGER DEFAULT 0, share_direction INTEGER DEFAULT 0, is_expirable INTEGER DEFAULT 0, expire_state INTEGER DEFAULT 0, message_action_type INTEGER DEFAULT 0, message_source INTEGER DEFAULT 0, associated_message_guid TEXT, associated_message_type INTEGER DEFAULT 0, balloon_bundle_id TEXT, payload_data BLOB, expressive_send_style_id TEXT, associated_message_range_location INTEGER DEFAULT 0, associated_message_range_length INTEGER DEFAULT 0, time_expressive_send_played INTEGER, message_summary_info BLOB);
CREATE TABLE IF NOT EXISTS chat (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, guid TEXT UNIQUE NOT NULL, style INTEGER, state INTEGER, account_id TEXT, properties BLOB, chat_identifier TEXT, service_name TEXT, room_name TEXT, account_login TEXT, is_archived INTEGER DEFAULT 0, last_addressed_handle TEXT, display_name TEXT, group_id TEXT, is_filtered INTEGER, successful_query INTEGER);
CREATE INDEX IF NOT EXISTS message_idx_is_read ON message(is_read, is_from_me, is_finished);
CREATE INDEX IF NOT EXISTS message_idx_date ON message(date);
CREATE INDEX IF NOT EXISTS chat_idx_chat_identifier_service_name ON chat(chat_identifier, service_name);
CREATE INDEX IF NOT EXISTS chat_idx_chat_identifier ON chat(chat_identifier);
CREATE INDEX IF NOT EXISTS message_idx_failed ON message(is_finished, is_from_me, error);
CREATE INDEX IF NOT EXISTS message_idx_was_downgraded ON message(was_downgraded);
CREATE INDEX IF NOT EXISTS chat_idx_chat_room_name_service_name ON chat(room_name, service_name);
CREATE INDEX IF NOT EXISTS chat_message_join_idx_message_id ON chat_message_join(message_id, chat_id);
CREATE INDEX IF NOT EXISTS chat_handle_join_idx_handle_id ON chat_handle_join(handle_id);
CREATE INDEX IF NOT EXISTS message_idx_handle ON message(handle_id, date);
CREATE INDEX IF NOT EXISTS message_idx_handle_id ON message(handle_id);
CREATE INDEX IF NOT EXISTS chat_message_join_idx_chat_id ON chat_message_join(chat_id);
CREATE INDEX IF NOT EXISTS message_idx_other_handle ON message(other_handle);
CREATE INDEX IF NOT EXISTS message_idx_expire_state ON message(expire_state);
CREATE INDEX IF NOT EXISTS chat_message_join_idx_message_id_only ON chat_message_join(message_id);
CREATE INDEX IF NOT EXISTS message_idx_associated_message ON message(associated_message_guid);
CREATE INDEX IF NOT EXISTS chat_idx_is_archived ON chat(is_archived);
CREATE TRIGGER IF NOT EXISTS after_delete_on_chat AFTER DELETE ON chat BEGIN DELETE FROM chat_message_join WHERE chat_id = OLD.ROWID; END;
CREATE TRIGGER IF NOT EXISTS delete_associated_messages_after_delete_on_message AFTER DELETE ON message BEGIN DELETE FROM message WHERE (OLD.associated_message_guid IS NULL AND associated_message_guid IS NOT NULL AND guid = OLD.associated_message_guid); END;
CREATE TRIGGER IF NOT EXISTS add_to_deleted_messages AFTER DELETE ON message BEGIN INSERT INTO deleted_messages (guid) VALUES (OLD.guid); END;
CREATE TRIGGER IF NOT EXISTS after_delete_on_chat_message_join AFTER DELETE ON chat_message_join BEGIN UPDATE message SET cache_roomnames = (SELECT group_concat(c.room_name) FROM chat c INNER JOIN chat_message_join j ON c.ROWID = j.chat_id WHERE j.message_id = OLD.message_id) WHERE message.ROWID = OLD.message_id; DELETE FROM message WHERE message.ROWID = OLD.message_id AND OLD.message_id NOT IN (SELECT chat_message_join.message_id from chat_message_join WHERE chat_message_join.message_id = OLD.message_id LIMIT 1); END;
CREATE TRIGGER IF NOT EXISTS after_delete_on_chat_handle_join AFTER DELETE ON chat_handle_join BEGIN DELETE FROM handle WHERE handle.ROWID = OLD.handle_id AND (SELECT 1 from chat_handle_join WHERE handle_id = OLD.handle_id LIMIT 1) IS NULL AND (SELECT 1 from message WHERE handle_id = OLD.handle_id LIMIT 1) IS NULL AND (SELECT 1 from message WHERE other_handle = OLD.handle_id LIMIT 1) IS NULL; END;
CREATE TRIGGER IF NOT EXISTS after_delete_on_message AFTER DELETE ON message BEGIN DELETE FROM handle WHERE handle.ROWID = OLD.handle_id AND (SELECT 1 from chat_handle_join WHERE handle_id = OLD.handle_id LIMIT 1) IS NULL AND (SELECT 1 from message WHERE handle_id = OLD.handle_id LIMIT 1) IS NULL AND (SELECT 1 from message WHERE other_handle = OLD.handle_id LIMIT 1) IS NULL; END;
CREATE TRIGGER IF NOT EXISTS after_insert_on_chat_message_join AFTER INSERT ON chat_message_join BEGIN UPDATE message SET cache_roomnames = (SELECT group_concat(c.room_name) FROM chat c INNER JOIN chat_message_join j ON c.ROWID = j.chat_id WHERE j.message_id = NEW.message_id) WHERE message.ROWID = NEW.message_id; END;
CREATE TABLE IF NOT EXISTS attachment (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, guid TEXT UNIQUE NOT NULL, created_date INTEGER DEFAULT 0, start_date INTEGER DEFAULT 0, filename TEXT, uti TEXT, mime_type TEXT, transfer_state INTEGER DEFAULT 0, is_outgoing INTEGER DEFAULT 0, user_info BLOB, transfer_name TEXT, total_bytes INTEGER DEFAULT 0, is_sticker INTEGER DEFAULT 0, sticker_user_info BLOB, attribution_info BLOB, hide_attachment INTEGER DEFAULT 0);
CREATE TRIGGER IF NOT EXISTS before_delete_on_attachment BEFORE DELETE ON attachment BEGIN SELECT before_delete_attachment_path(OLD.ROWID, OLD.guid); END;
CREATE TRIGGER IF NOT EXISTS after_delete_on_attachment AFTER DELETE ON attachment BEGIN SELECT delete_attachment_path(OLD.filename); END;
CREATE TABLE IF NOT EXISTS message_attachment_join (message_id INTEGER REFERENCES message (ROWID) ON DELETE CASCADE, attachment_id INTEGER REFERENCES attachment (ROWID) ON DELETE CASCADE, UNIQUE(message_id, attachment_id));
CREATE INDEX IF NOT EXISTS message_attachment_join_idx_message_id ON message_attachment_join(message_id);
CREATE INDEX IF NOT EXISTS message_attachment_join_idx_attachment_id ON message_attachment_join(attachment_id);
CREATE TRIGGER IF NOT EXISTS after_delete_on_message_attachment_join AFTER DELETE ON message_attachment_join BEGIN DELETE FROM attachment WHERE attachment.ROWID = OLD.attachment_id AND (SELECT 1 from message_attachment_join WHERE attachment_id = OLD.attachment_id LIMIT 1) IS NULL; END;
CREATE TRIGGER IF NOT EXISTS after_insert_on_message_attachment_join AFTER INSERT ON message_attachment_join BEGIN UPDATE message SET cache_has_attachments = 1 WHERE message.ROWID = NEW.message_id; END;
CREATE TABLE IF NOT EXISTS message_update_date_join (message_id INTEGER REFERENCES message (ROWID) ON DELETE CASCADE, message_update_date INTEGER DEFAULT 0, PRIMARY KEY (message_id, message_update_date));
CREATE TRIGGER IF NOT EXISTS insert_last_update_date AFTER INSERT ON message BEGIN INSERT INTO message_update_date_join (message_id, message_update_date) VALUES ( NEW.ROWID, strftime('%s','now') ); END;
CREATE TRIGGER IF NOT EXISTS update_last_update_date AFTER UPDATE ON message BEGIN UPDATE message_update_date_join SET message_update_date = strftime('%s','now') WHERE message_id = OLD.ROWID; END;
END_DOCUMENT
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Failed to create a database at ${DBNAME} on the local machine."
else
pass "Done creating a database at ${DBNAME}."
CREATED_LOCAL_DB=1
fi
}
function test_ssh {
# Check that USERNAME@IPADDR is reachable.
ssh -q -o ConnectTimeout=10 "${USERNAME}"@"${IPADDR}" exit
OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
# If it is not reachable, ask if you want to continue.
fail "Failed to login as ${USERNAME} on the machine ${IPADDR}."
else
# If it is, continue with installation.
pass "Successfully logged in as ${USERNAME} on the machine ${IPADDR}"
fi
}
function begin {
echo -e "\033[1;37m$1\033[0m"
}
function fail {
echo -e "\033[1;31m$1\033[0m"
}
function pass {
echo -e "\033[1;32m$1\033[0m"
}
function scp_and_check {
# Take in optional q and r vars (quiet and recursive flags)
# Note that q is on by default, and r off by default.
# Passing anything in as q or r sets q or r
q=${q:+"-q"}
r=${r:+"-r"}
local from="$1"
local to="$2"
scp $q $r "${from}" "${to}"
local OUTPUT=$?
if [ $OUTPUT -ne 0 ]
then
fail "Error encountered transferring ${from} to ${to}."
echo 1
else
echo 0
fi
}
main