Passed
Push — master ( 47e36c...1b8f90 )
by
unknown
06:00 queued 18s
created

IndexSqlite   F

Complexity

Total Complexity 116

Size/Duplication

Total Lines 657
Duplicated Lines 0 %

Importance

Changes 8
Bugs 2 Features 1
Metric Value
eloc 438
c 8
b 2
f 1
dl 0
loc 657
rs 2
wmc 116

15 Methods

Rating   Name   Duplication   Size   Complexity  
A get_gc_value() 0 10 1
A __construct() 0 4 1
A result_full() 0 2 1
C refresh() 0 95 12
A finalize() 0 4 1
A remove() 0 17 3
A precompile_insert() 0 14 2
C bind_insert() 0 73 9
F insert_message() 0 163 32
A refresh_hierarchy() 0 14 2
C try_insert_content() 0 51 16
A create() 0 7 1
A load() 0 34 5
F search() 0 130 29
A quote_words() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like IndexSqlite often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use IndexSqlite, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
define('PRIVATE_FID_ROOT', 0x1);
4
5
define('PR_FOLDER_ID', 0x67480014);
6
define('PR_MID', 0x674A0014);
7
define('PR_CHANGE_NUMBER', 0x67A40014);
8
9
class IndexSqlite extends SQLite3 {
10
	private $username;
11
	private $stmt;
12
	private $count;
13
	private $store;
14
	private $session;
15
	private $hide_attachments_proptag;
0 ignored issues
show
introduced by
The private property $hide_attachments_proptag is not used, and could be removed.
Loading history...
16
17
	private static function get_gc_value($eid) {
18
		$r0 = ($eid >> 56) & 0xFF;
19
		$r1 = ($eid >> 48) & 0xFF;
20
		$r2 = ($eid >> 40) & 0xFF;
21
		$r3 = ($eid >> 32) & 0xFF;
22
		$r4 = ($eid >> 24) & 0xFF;
23
		$r5 = ($eid >> 16) & 0xFF;
24
		$value = $r0 | ($r1 << 8) | ($r2 << 16) | ($r3 << 24) | ($r4 << 32) | ($r5 << 40);
25
26
		return $value;
27
	}
28
29
	public function __construct($username = null, $session = null, $store = null) {
30
		$this->username = $username ?? $GLOBALS["mapisession"]->getSMTPAddress();
31
		$this->session = $session ?? $GLOBALS["mapisession"]->getSession();
32
		$this->store = $store ?? $GLOBALS["mapisession"]->getDefaultMessageStore();
33
	}
34
35
	private function try_insert_content(
36
		$search_entryid,
37
		$row,
38
		$folder_id,
39
		$recursive,
40
		$message_classes,
41
		$date_start,
42
		$date_end,
43
		$unread,
44
		$has_attachments
45
	) {
46
		// if match condition contains '@', $row['entryid'] will disappear. it seems a bug for php-sqlite
47
		if (strlen($row['entryid']) == 0) {
48
			$results = $this->query("SELECT entryid FROM messages WHERE message_id=" . $row['message_id']);
49
			$row1 = $results->fetchArray(SQLITE3_NUM);
50
			if ($row1) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row1 of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
51
				$row['entryid'] = $row1[0];
52
			}
53
		}
54
		if (isset($message_classes)) {
55
			$found = false;
56
			foreach ($message_classes as $message_class) {
57
				if (strncasecmp($row['message_class'], $message_class, strlen($message_class)) == 0) {
58
					$found = true;
59
					break;
60
				}
61
			}
62
			if (!$found) {
63
				return;
64
			}
65
		}
66
		if (isset($date_start) && $row['date'] < $date_start) {
67
			return;
68
		}
69
		if (isset($date_end) && $row['date'] > $date_end) {
70
			return;
71
		}
72
		if (isset($unread) && $row['readflag']) {
73
			return;
74
		}
75
		if (isset($has_attachments) && !$row['attach_indexed']) {
76
			return;
77
		}
78
79
		try {
80
			mapi_linkmessage($this->session, $search_entryid, $row['entryid']);
81
		}
82
		catch (Exception $e) {
83
			return;
84
		}
85
		++$this->count;
86
	}
87
88
	private function result_full() {
89
		return $this->count >= MAX_FTS_RESULT_ITEMS;
90
	}
91
92
	public function search(
93
		$search_entryid,
94
		$sender,
95
		$sending,
96
		$recipients,
97
		$subject,
98
		$content,
99
		$attachments,
100
		$others,
101
		$folder_entrid,
102
		$recursive,
103
		$message_classes,
104
		$date_start,
105
		$date_end,
106
		$unread,
107
		$has_attachments
108
	) {
109
		$search_folder = mapi_msgstore_openentry($this->store, $search_entryid);
110
		$tmp_props = mapi_getprops($search_folder, [PR_FOLDER_ID]);
0 ignored issues
show
Unused Code introduced by
The assignment to $tmp_props is dead and can be removed.
Loading history...
111
		if (isset($folder_entrid)) {
112
			try {
113
				$folder = mapi_msgstore_openentry($this->store, $folder_entrid);
114
				if (!$folder) {
115
					return false;
116
				}
117
				$tmp_props = mapi_getprops($folder, [PR_FOLDER_ID]);
118
				if (empty($tmp_props[PR_FOLDER_ID])) {
119
					return false;
120
				}
121
				$folder_id = IndexSqlite::get_gc_value((int) $tmp_props[PR_FOLDER_ID]);
122
			}
123
			catch (Exception $e) {
124
				return false;
125
			}
126
		}
127
		$sql_string = "SELECT message_id, entryid, folder_id, sender, sending, " .
128
			"recipients, subject, content, attachments, message_class, date, attach_indexed, readflag FROM" .
129
			" messages WHERE messages MATCH '";
130
		$this->count = 0;
131
		if (isset($sender) && $sender == $sending && $sending == $recipients && $recipients == $subject &&
132
			$subject == $content && $content == $attachments && $attachments == $others) {
133
			$sql_string .= SQLite3::escapeString($this->quote_words($sender)) . "'";
134
		}
135
		else {
136
			$first = true;
137
			if (isset($sender)) {
138
				if ($first === true) {
0 ignored issues
show
introduced by
The condition $first === true is always true.
Loading history...
139
					$first = false;
140
				}
141
				else {
142
					$sql_string .= " OR ";
143
				}
144
				$sql_string .= 'sender:' . SQLite3::escapeString($this->quote_words($sender));
145
			}
146
			if (isset($sending)) {
147
				if ($first === true) {
148
					$first = false;
149
				}
150
				else {
151
					$sql_string .= " OR ";
152
				}
153
				$sql_string .= 'sending:' . SQLite3::escapeString($this->quote_words($sending));
154
			}
155
			if (isset($recipients)) {
156
				if ($first === true) {
157
					$first = false;
158
				}
159
				else {
160
					$sql_string .= " OR ";
161
				}
162
				$sql_string .= 'recipients:' . SQLite3::escapeString($this->quote_words($recipients));
163
			}
164
			if (isset($subject)) {
165
				if ($first === true) {
166
					$first = false;
167
				}
168
				else {
169
					$sql_string .= " OR ";
170
				}
171
				$sql_string .= 'subject:' . SQLite3::escapeString($this->quote_words($subject));
172
			}
173
			if (isset($content)) {
174
				if ($first === true) {
175
					$first = false;
176
				}
177
				else {
178
					$sql_string .= " OR ";
179
				}
180
				$sql_string .= 'content:' . SQLite3::escapeString($this->quote_words($content));
181
			}
182
			if (isset($attachments)) {
183
				if ($first === true) {
184
					$first = false;
185
				}
186
				else {
187
					$sql_string .= " OR ";
188
				}
189
				$sql_string .= 'attachments:' . SQLite3::escapeString($this->quote_words($attachments));
190
			}
191
			if (isset($others)) {
192
				if ($first === true) {
193
					$first = false;
194
				}
195
				else {
196
					$sql_string .= " OR ";
197
				}
198
				$sql_string .= 'others:' . SQLite3::escapeString($this->quote_words($others));
199
			}
200
			if ($first) {
201
				return false;
202
			}
203
			$sql_string .= "'";
204
		}
205
		$sql_string .= " ORDER BY date DESC LIMIT " . MAX_FTS_RESULT_ITEMS;
206
		$results = $this->query($sql_string);
207
		while (($row = $results->fetchArray(SQLITE3_ASSOC)) && !$this->result_full()) {
208
			$this->try_insert_content(
209
				$search_entryid,
210
				$row,
211
				$folder_id,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $folder_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
212
				$recursive,
213
				$message_classes,
214
				$date_start,
215
				$date_end,
216
				$unread,
217
				$has_attachments
218
			);
219
		}
220
221
		return true;
222
	}
223
224
	private function create() {
225
		mkdir(SQLITE_INDEX_PATH . '/' . $this->username);
226
		chmod(SQLITE_INDEX_PATH . '/' . $this->username, 0770);
227
		$this->open(SQLITE_INDEX_PATH . '/' . $this->username . '/index.sqlite3');
228
		chmod(SQLITE_INDEX_PATH . '/' . $this->username . '/index.sqlite3', 0660);
229
230
		return true;
231
	}
232
233
	public function load() {
234
		if (!is_file(SQLITE_INDEX_PATH . '/' . $this->username . '/index.sqlite3')) {
235
			if ($this->create() === false) {
0 ignored issues
show
introduced by
The condition $this->create() === false is always false.
Loading history...
236
				return false;
237
			}
238
		}
239
		else {
240
			$this->open(SQLITE_INDEX_PATH . '/' . $this->username . '/index.sqlite3');
241
		}
242
		$sql_string = "CREATE TABLE IF NOT EXISTS hierarchy(" .
243
				"folder_id INTEGER PRIMARY KEY," .
244
				"commit_max INTEGER NOT NULL," .
245
				"max_cn INTEGER NOT NULL);\n" .
246
				"CREATE VIRTUAL TABLE IF NOT EXISTS messages USING " .
247
				SQLITE_FTS_ENGINE .
248
				"(sender, sending, recipients, " .
249
				"subject, content, attachments," .
250
				"others, message_id," .
251
				"attach_indexed UNINDEXED," .
252
				"entryid UNINDEXED," .
253
				"change_num UNINDEXED," .
254
				"folder_id UNINDEXED," .
255
				"message_class UNINDEXED," .
256
				"date UNINDEXED, " .
257
				"readflag UNINDEXED, " .
258
				"tokenize=" . SQLITE_FTS_TOKENIZER . ");";
259
		if ($this->exec($sql_string) === false) {
260
			error_log("fail to execute sqlite create table statemente, " . $this->lastErrorMsg());
261
262
			return false;
263
		}
264
265
		// refresh the index sqlite database if configured
266
		return REFRESH_SEARCH_INDEX ? $this->refresh() : true;
267
	}
268
269
	private function refresh() {
270
		$mapping = [
271
			"categories" => "PT_MV_STRING8:PS_PUBLIC_STRINGS:Keywords",
272
			"fileas" => "PT_STRING8:PSETID_Address:0x8005",
273
			"location" => "PT_STRING8:PSETID_Appointment:" . PidLidLocation,
274
			"email1" => "PT_STRING8:PSETID_Address:" . PidLidEmail1EmailAddress,
275
			"emai1_name" => "PT_STRING8:PSETID_Address:" . PidLidEmail1DisplayName,
276
			"email2" => "PT_STRING8:PSETID_Address:" . PidLidEmail2EmailAddress,
277
			"email2_name" => "PT_STRING8:PSETID_Address:" . PidLidEmail2DisplayName,
278
			"email3" => "PT_STRING8:PSETID_Address:" . PidLidEmail3EmailAddress,
279
			"email3_name" => "PT_STRING8:PSETID_Address:" . PidLidEmail3DisplayName,
280
			"home_address" => "PT_STRING8:PSETID_Address:0x801a",
281
			"other_address" => "PT_STRING8:PSETID_Address:0x801c",
282
			"work_address" => "PT_STRING8:PSETID_Address:0x801b",
283
			"task_owner" => "PT_STRING8:PSETID_Task:0x811f",
284
			"companies" => "PT_MV_STRING8:PSETID_Common:0x8539",
285
		];
286
		$properties = getPropIdsFromStrings($this->store, $mapping);
287
		$store_props = mapi_getprops($this->store, [PR_IPM_SUBTREE_ENTRYID]);
288
		$entryid = $store_props[PR_IPM_SUBTREE_ENTRYID];
289
290
		try {
291
			$ipm_subtree = mapi_msgstore_openentry($this->store, $entryid);
292
			$table = mapi_folder_gethierarchytable($ipm_subtree, CONVENIENT_DEPTH);
293
		}
294
		catch (Exception $e) {
295
			error_log("fail to refresh indexing sqlite, cannot open ipmsubstree hierarchy table in " . $this->username . "'s store");
296
297
			return false;
298
		}
299
		$stmt = $this->prepare("SELECT commit_max, max_cn FROM hierarchy WHERE folder_id=:folder_id");
300
		$items = mapi_table_queryallrows($table, [PR_ENTRYID,
301
			PR_FOLDER_ID, PR_FOLDER_TYPE, PR_LOCAL_COMMIT_TIME_MAX, ]);
302
		$hierarchy = [];
303
		$messages = [];
304
		foreach ($items as $item) {
305
			if ($item[PR_FOLDER_TYPE] != FOLDER_GENERIC) {
306
				continue;
307
			}
308
			$max_cn = 0;
309
			$last_cn = 0;
310
			$folder_id = IndexSqlite::get_gc_value((int) $item[PR_FOLDER_ID]);
311
			$stmt->reset();
312
			$stmt->bindValue(":folder_id", $folder_id, SQLITE3_INTEGER);
313
			$ret = $stmt->execute();
314
			$row = $ret->fetchArray(SQLITE3_ASSOC);
315
			if ($row) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
316
				$max_cn = $row['max_cn'];
317
				$last_cn = $max_cn;
318
				if ($row['commit_max'] == $item[PR_LOCAL_COMMIT_TIME_MAX]) {
319
					continue;
320
				}
321
			}
322
323
			try {
324
				$folder = mapi_msgstore_openentry($this->store, $item[PR_ENTRYID]);
325
				$table = mapi_folder_getcontentstable($folder);
326
				$contents = mapi_table_queryallrows($table, [PR_MID, PR_CHANGE_NUMBER, PR_ENTRYID]);
327
				foreach ($contents as $content) {
328
					$change_num = IndexSqlite::get_gc_value((int) $content[PR_CHANGE_NUMBER]);
329
					if ($change_num > $last_cn) {
330
						if ($change_num > $max_cn) {
331
							$max_cn = $change_num;
332
						}
333
						$messages[] = [IndexSqlite::get_gc_value((int) $content[PR_MID]), $content[PR_ENTRYID]];
334
					}
335
				}
336
			}
337
			catch (Exception $e) {
338
				error_log("fail to refresh indexing sqlite, cannot load contents for folder " . $folder_id . " in " . $this->username . "'s store");
339
340
				return false;
341
			}
342
			$hierarchy[] = [$folder_id, $item[PR_LOCAL_COMMIT_TIME_MAX], $max_cn];
343
		}
344
		$stmt->close();
345
346
		$this->remove($messages);
347
		$this->precompile_insert();
348
		$count = count($messages);
349
		for ($i = 0; $i < $count; ++$i) {
350
			try {
351
				$message = mapi_msgstore_openentry($this->store, $messages[$i][1]);
352
				$this->insert_message($messages[$i][0], $message, $properties);
353
			}
354
			catch (Exception $e) {
355
				error_log("fail to insert message " . $messages[$i][0] . " into index sqlite for " . $this->username);
356
357
				continue;
358
			}
359
		}
360
		$this->finalize();
361
		$this->refresh_hierarchy($hierarchy);
362
363
		return true;
364
	}
365
366
	public function insert_message($message_id, $message, $properties) {
367
		$others_tags = [
368
			PR_DISPLAY_NAME,
369
			PR_DISPLAY_NAME_PREFIX,
370
			PR_HOME_TELEPHONE_NUMBER,
371
			PR_MOBILE_TELEPHONE_NUMBER,
372
			PR_BUSINESS_TELEPHONE_NUMBER,
373
			PR_BUSINESS_FAX_NUMBER,
374
			PR_ASSISTANT_TELEPHONE_NUMBER,
375
			PR_BUSINESS2_TELEPHONE_NUMBER,
376
			PR_CALLBACK_TELEPHONE_NUMBER,
377
			PR_CAR_TELEPHONE_NUMBER,
378
			PR_COMPANY_MAIN_PHONE_NUMBER,
379
			PR_HOME2_TELEPHONE_NUMBER,
380
			PR_HOME_FAX_NUMBER,
381
			PR_OTHER_TELEPHONE_NUMBER,
382
			PR_PAGER_TELEPHONE_NUMBER,
383
			PR_PRIMARY_FAX_NUMBER,
384
			PR_PRIMARY_TELEPHONE_NUMBER,
385
			PR_RADIO_TELEPHONE_NUMBER,
386
			PR_TELEX_NUMBER,
387
			PR_TTYTDD_PHONE_NUMBER,
388
			PR_COMPANY_NAME,
389
			PR_TITLE, ];
390
		$proptags = array_merge(
391
			[PR_ENTRYID,
392
				PR_SENT_REPRESENTING_NAME,
393
				PR_SENT_REPRESENTING_SMTP_ADDRESS,
394
				PR_SUBJECT,
395
				PR_BODY,
396
				PR_HTML,
397
				PR_SENDER_NAME,
398
				PR_SENDER_SMTP_ADDRESS,
399
				PR_INTERNET_CPID,
400
				PR_RTF_COMPRESSED,
401
				PR_CHANGE_NUMBER,
402
				PR_FOLDER_ID,
403
				PR_MESSAGE_CLASS,
404
				PR_MESSAGE_DELIVERY_TIME,
405
				PR_LAST_MODIFICATION_TIME,
406
				PR_MESSAGE_FLAGS, ],
407
			array_values($properties)
408
		);
409
		$proptags = array_merge($proptags, $others_tags);
410
		$propvals = mapi_getprops($message, $proptags);
411
		$entryid = $propvals[PR_ENTRYID];
412
		$table = mapi_message_getrecipienttable($message);
413
		$recips = mapi_table_queryallrows($table, [PR_DISPLAY_NAME, PR_SMTP_ADDRESS]);
414
		$recipients_string = '';
415
		$first = true;
416
		foreach ($recips as $recip) {
417
			if (!$first) {
418
				$recipients_string .= "\n";
419
			}
420
			$first = false;
421
			if (isset($recip[PR_DISPLAY_NAME])) {
422
				$recipients_string .= $recip[PR_DISPLAY_NAME];
423
			}
424
			if (isset($recip[PR_SMTP_ADDRESS])) {
425
				$recipients_string .= "\n" . $recip[PR_SMTP_ADDRESS];
426
			}
427
		}
428
		$table = mapi_message_getattachmenttable($message);
429
		$attachments = mapi_table_queryallrows($table, [PR_ATTACH_LONG_FILENAME]);
430
		$attachments_string = '';
431
		$first = true;
432
		foreach ($attachments as $attach) {
433
			if (!$first) {
434
				$attachments_string .= "\n";
435
			}
436
			if (isset($attach[PR_ATTACH_LONG_FILENAME])) {
437
				$first = false;
438
				$attachments_string .= $attach[PR_ATTACH_LONG_FILENAME];
439
			}
440
		}
441
		$sending = null;
442
		if (isset($propvals[PR_SENT_REPRESENTING_NAME], $propvals[PR_SENT_REPRESENTING_SMTP_ADDRESS])) {
443
			$sending = $propvals[PR_SENT_REPRESENTING_NAME] . "\n" . $propvals[PR_SENT_REPRESENTING_SMTP_ADDRESS];
444
		}
445
		elseif (isset($propvals[PR_SENT_REPRESENTING_NAME])) {
446
			$sending = $propvals[PR_SENT_REPRESENTING_NAME];
447
		}
448
		elseif (isset($propvals[PR_SENT_REPRESENTING_SMTP_ADDRESS])) {
449
			$sending = "\n" . $propvals[PR_SENT_REPRESENTING_SMTP_ADDRESS];
450
		}
451
		$sender = null;
452
		if (isset($propvals[PR_SENDER_NAME], $propvals[PR_SENDER_SMTP_ADDRESS])) {
453
			$sender = $propvals[PR_SENDER_NAME] . "\n" . $propvals[PR_SENDER_SMTP_ADDRESS];
454
		}
455
		elseif (isset($propvals[PR_SENDER_NAME])) {
456
			$sender = $propvals[PR_SENDER_NAME];
457
		}
458
		elseif (isset($propvals[PR_SENDER_SMTP_ADDRESS])) {
459
			$sender = "\n" . $propvals[PR_SENDER_SMTP_ADDRESS];
460
		}
461
		$subject = null;
462
		if (isset($propvals[PR_SUBJECT])) {
463
			$subject = $propvals[PR_SUBJECT];
464
		}
465
		$html = null;
466
		if (isset($propvals[PR_HTML])) {
467
			$cpid = $propvals[PR_INTERNET_CPID];
468
			if (empty($cpid)) {
469
				$cpid = 65001;
470
			}
471
			$html = Conversion::convertCodepageStringToUtf8($cpid, $propvals[PR_HTML]);
472
		}
473
		if (!$html && isset($propvals[PR_RTF_COMPRESSED])) {
474
			$html = mapi_decompressrtf($propvals[PR_RTF_COMPRESSED]);
475
		}
476
		if ($html) {
477
			$body = strip_tags($html);
478
		}
479
		else {
480
			if (isset($propvals[PR_BODY])) {
481
				$body = $propvals[PR_BODY];
482
			}
483
		}
484
		$others = '';
485
		foreach ($properties as $name => $proptag) {
486
			if (isset($propvals[$proptag])) {
487
				if ($name == 'companies' || $name == 'categories') {
488
					$others .= implode("\n", $propvals[$proptag]) . "\n";
489
				}
490
				else {
491
					$others .= $propvals[$proptag] . "\n";
492
				}
493
			}
494
		}
495
		foreach ($others_tags as $proptag) {
496
			if (isset($propvals[$proptag])) {
497
				$others .= $propvals[$proptag] . "\n";
498
			}
499
		}
500
		if (empty($propvals[PR_MESSAGE_DELIVERY_TIME])) {
501
			$last_time = $propvals[PR_LAST_MODIFICATION_TIME];
502
		}
503
		else {
504
			$last_time = $propvals[PR_MESSAGE_DELIVERY_TIME];
505
		}
506
		if (empty($propvals[PR_FOLDER_ID]) ||
507
			empty($propvals[PR_CHANGE_NUMBER]) ||
508
			empty($propvals[PR_MESSAGE_CLASS])) {
509
			return;
510
		}
511
		$readflag = isset($propvals[PR_MESSAGE_FLAGS]) ?
512
			$propvals[PR_MESSAGE_FLAGS] & MSGFLAG_READ : 1;
513
514
		$this->bind_insert(
515
			$sender,
516
			$sending,
517
			$recipients_string,
518
			$subject,
519
			$body,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $body does not seem to be defined for all execution paths leading up to this point.
Loading history...
520
			$attachments_string,
521
			$others,
522
			$message_id,
523
			$entryid,
524
			IndexSqlite::get_gc_value((int) $propvals[PR_CHANGE_NUMBER]),
525
			IndexSqlite::get_gc_value((int) $propvals[PR_FOLDER_ID]),
526
			$propvals[PR_MESSAGE_CLASS],
527
			$last_time,
528
			$readflag
529
		);
530
	}
531
532
	private function precompile_insert() {
533
		$this->stmt = $this->prepare("INSERT INTO messages (sender, sending, recipients, subject, " .
534
			"content, attachments, others, message_id, attach_indexed, entryid, change_num, folder_id," .
535
			" message_class, date, readflag) VALUES (:sender, :sending, :recipients, :subject, :content, " .
536
			":attachments, :others, :message_id, :attach_indexed, :entryid, :change_num, :folder_id, " .
537
			":message_class, :date, :readflag)");
538
		if (!$this->stmt) {
539
			error_log("fail to precompile the insert statement for messages table, " . $this->lastErrorMsg());
540
541
			return false;
542
		}
543
		$this->exec("BEGIN TRANSACTION");
544
545
		return true;
546
	}
547
548
	private function bind_insert(
549
		$sender,
550
		$sending,
551
		$recipients,
552
		$subject,
553
		$content,
554
		$attachments,
555
		$others,
556
		$message_id,
557
		$entryid,
558
		$change_num,
559
		$folder_id,
560
		$message_class,
561
		$date,
562
		$readflag
563
	) {
564
		$this->stmt->clear();
565
		if (isset($sender)) {
566
			$this->stmt->bindValue(":sender", $sender, SQLITE3_TEXT);
567
		}
568
		else {
569
			$this->stmt->bindValue(":sender", $sender, SQLITE3_NULL);
570
		}
571
		if (isset($sending)) {
572
			$this->stmt->bindValue(":sending", $sending, SQLITE3_TEXT);
573
		}
574
		else {
575
			$this->stmt->bindValue(":sending", $sending, SQLITE3_NULL);
576
		}
577
		if (isset($recipients)) {
578
			$this->stmt->bindValue(":recipients", $recipients, SQLITE3_TEXT);
579
		}
580
		else {
581
			$this->stmt->bindValue(":recipients", $recipients, SQLITE3_NULL);
582
		}
583
		if (isset($subject)) {
584
			$this->stmt->bindValue(":subject", $subject, SQLITE3_TEXT);
585
		}
586
		else {
587
			$this->stmt->bindValue(":subject", $subject, SQLITE3_NULL);
588
		}
589
		if (isset($content)) {
590
			$this->stmt->bindValue(":content", $content, SQLITE3_TEXT);
591
		}
592
		else {
593
			$this->stmt->bindValue(":content", $content, SQLITE3_NULL);
594
		}
595
		if (isset($attachments)) {
596
			$this->stmt->bindValue(":attachments", $attachments, SQLITE3_TEXT);
597
		}
598
		else {
599
			$this->stmt->bindValue(":attachments", $attachments, SQLITE3_NULL);
600
		}
601
		if (isset($others)) {
602
			$this->stmt->bindValue(":others", $others, SQLITE3_TEXT);
603
		}
604
		else {
605
			$this->stmt->bindValue(":others", $others, SQLITE3_NULL);
606
		}
607
		$this->stmt->bindValue(":message_id", $message_id, SQLITE3_INTEGER);
608
		if (isset($attachments)) {
609
			$this->stmt->bindValue(":attach_indexed", 0, SQLITE3_INTEGER);
610
		}
611
		else {
612
			$this->stmt->bindValue(":attach_indexed", 1, SQLITE3_INTEGER);
613
		}
614
		$this->stmt->bindValue(":entryid", $entryid, SQLITE3_BLOB);
615
		$this->stmt->bindValue(":change_num", $change_num, SQLITE3_INTEGER);
616
		$this->stmt->bindValue(":folder_id", $folder_id, SQLITE3_INTEGER);
617
		$this->stmt->bindValue(":message_class", $message_class, SQLITE3_TEXT);
618
		$this->stmt->bindValue(":date", $date, SQLITE3_INTEGER);
619
		$this->stmt->bindValue(":readflag", $readflag, SQLITE3_INTEGER);
620
		$this->stmt->execute();
621
	}
622
623
	private function finalize() {
624
		$this->exec("COMMIT TRANSACTION");
625
		$this->stmt->close();
626
		unset($this->stmt);
627
	}
628
629
	private function remove($deletion) {
630
		$stmt = $this->prepare("DELETE FROM messages WHERE message_id=:message_id");
631
		if (!$stmt) {
0 ignored issues
show
introduced by
$stmt is of type SQLite3Stmt, thus it always evaluated to true.
Loading history...
632
			error_log("fail to precompile the delete statement for messages table, " . $this->lastErrorMsg());
633
634
			return false;
635
		}
636
		$this->exec("BEGIN TRANSACTION");
637
		$count = count($deletion);
638
		for ($i = 0; $i < $count; ++$i) {
639
			$stmt->clear();
640
			$stmt->bindValue(":message_id", $deletion[$i][0], SQLITE3_INTEGER);
641
			$stmt->execute();
642
		}
643
		$this->exec("COMMIT TRANSACTION");
644
645
		return true;
646
	}
647
648
	private function refresh_hierarchy($hierarchy) {
649
		$stmt = $this->prepare("REPLACE INTO hierarchy (folder_id, commit_max, max_cn) VALUES (:folder_id, :commit_max, :max_cn)");
650
		$this->exec("BEGIN TRANSACTION");
651
		$count = count($hierarchy);
652
		for ($i = 0; $i < $count; ++$i) {
653
			$stmt->clear();
654
			$stmt->bindValue(":folder_id", $hierarchy[$i][0], SQLITE3_INTEGER);
655
			$stmt->bindValue(":commit_max", $hierarchy[$i][1], SQLITE3_INTEGER);
656
			$stmt->bindValue(":max_cn", $hierarchy[$i][2], SQLITE3_INTEGER);
657
			$stmt->execute();
658
		}
659
		$this->exec("COMMIT TRANSACTION");
660
661
		return true;
662
	}
663
664
	private function quote_words($search_string) {
665
		return '"' . preg_replace("/(\\s+)/", '*" "', trim($search_string)) . '"*';
666
	}
667
}
668