1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
---------------------------------------------------------------------- |
5
|
|
|
AlternC - Web Hosting System |
6
|
|
|
Copyright (C) 2000-2012 by the AlternC Development Team. |
7
|
|
|
https://alternc.org/ |
8
|
|
|
---------------------------------------------------------------------- |
9
|
|
|
LICENSE |
10
|
|
|
|
11
|
|
|
This program is free software; you can redistribute it and/or |
12
|
|
|
modify it under the terms of the GNU General Public License (GPL) |
13
|
|
|
as published by the Free Software Foundation; either version 2 |
14
|
|
|
of the License, or (at your option) any later version. |
15
|
|
|
|
16
|
|
|
This program is distributed in the hope that it will be useful, |
17
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
18
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19
|
|
|
GNU General Public License for more details. |
20
|
|
|
|
21
|
|
|
To read the license please visit http://www.gnu.org/copyleft/gpl.html |
22
|
|
|
---------------------------------------------------------------------- |
23
|
|
|
Purpose of file: Manage Email accounts and aliases. |
24
|
|
|
---------------------------------------------------------------------- |
25
|
|
|
*/ |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* This class handle emails (pop and/or aliases and even wrapper for internal |
29
|
|
|
* classes) of hosted users. |
30
|
|
|
* |
31
|
|
|
* @copyright AlternC-Team 2012-09-01 http://alternc.com/ |
32
|
|
|
* This class is directly using the following alternc MySQL tables: |
33
|
|
|
* address = any used email address will be defined here, mailbox = pop/imap mailboxes, recipient = redirection from an email to another |
34
|
|
|
* and indirectly the domain class, to know domain names from their id in the DB. |
35
|
|
|
* This class is also defining a few hooks, search ->invoke in the code. |
36
|
|
|
*/ |
37
|
|
|
class m_mail { |
38
|
|
|
/* ----------------------------------------------------------------- */ |
39
|
|
|
|
40
|
|
|
/** domain list for this account |
41
|
|
|
* @access private |
42
|
|
|
*/ |
43
|
|
|
var $domains; |
44
|
|
|
|
45
|
|
|
/* ----------------------------------------------------------------- */ |
46
|
|
|
|
47
|
|
|
/** If an email has those chars, 'not nice in shell env' ;) |
48
|
|
|
* we don't store the email in $mail/u/{user}_domain, but in $mail/_/{address_id}_domain |
49
|
|
|
* @access private |
50
|
|
|
*/ |
51
|
|
|
var $specialchars = array('"', "'", '\\', '/'); |
52
|
|
|
|
53
|
|
|
/* ----------------------------------------------------------------- */ |
54
|
|
|
|
55
|
|
|
/** If an email has those chars, we will ONLY allow RECIPIENTS, NOT POP/IMAP for DOVECOT ! |
56
|
|
|
* Since Dovecot doesn't allow those characters |
57
|
|
|
* @access private |
58
|
|
|
*/ |
59
|
|
|
var $forbiddenchars = array('"', "'", '\\', '/', '?', '!', '*', '$', '|', '#', '+'); |
60
|
|
|
|
61
|
|
|
/* ----------------------------------------------------------------- */ |
62
|
|
|
|
63
|
|
|
/** Number of results for a pager display |
64
|
|
|
* @access public |
65
|
|
|
*/ |
66
|
|
|
var $total; |
67
|
|
|
// Human server name for help |
68
|
|
|
var $srv_submission; |
69
|
|
|
var $srv_smtp; |
70
|
|
|
var $srv_smtps; |
71
|
|
|
var $srv_imap; |
72
|
|
|
var $srv_imaps; |
73
|
|
|
var $srv_pop3; |
74
|
|
|
var $srv_pop3s; |
75
|
|
|
var $cache_domain_mail_size = array(); |
76
|
|
|
var $enum_domains = array(); |
77
|
|
|
|
78
|
|
|
/* ----------------------------------------------------------------- */ |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Constructeur |
82
|
|
|
*/ |
83
|
|
|
function m_mail() { |
84
|
|
|
global $L_FQDN; |
85
|
|
|
$this->srv_submission = variable_get('mail_human_submission', $L_FQDN, 'Human name for mail server (submission protocol), leave empty to disable help', array('desc' => 'Name', 'type' => 'string')); |
86
|
|
|
$this->srv_smtp = variable_get('mail_human_smtp', $L_FQDN, 'Human name for mail server (SMTP protocol), leave empty to disable help', array('desc' => 'Name', 'type' => 'string')); |
87
|
|
|
$this->srv_smtps = variable_get('mail_human_smtps', $L_FQDN, 'Human name for mail server (SMTPS protocol), leave empty to disable help', array('desc' => 'Name', 'type' => 'string')); |
88
|
|
|
$this->srv_imap = variable_get('mail_human_imap', $L_FQDN, 'Human name for IMAP mail server', array('desc' => 'Name', 'type' => 'string')); |
89
|
|
|
$this->srv_imaps = variable_get('mail_human_imaps', $L_FQDN, 'Human name for IMAPS mail server', array('desc' => 'Name', 'type' => 'string')); |
90
|
|
|
$this->srv_pop3 = variable_get('mail_human_pop3', $L_FQDN, 'Human name for POP3 mail server', array('desc' => 'Name', 'type' => 'string')); |
91
|
|
|
$this->srv_pop3s = variable_get('mail_human_pop3s', $L_FQDN, 'Human name for POP3s mail server', array('desc' => 'Name', 'type' => 'string')); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
function hook_menu() { |
95
|
|
|
$obj = array( |
96
|
|
|
'title' => _("Email Addresses"), |
97
|
|
|
'ico' => 'images/mail.png', |
98
|
|
|
'link' => 'toggle', |
99
|
|
|
'pos' => 30, |
100
|
|
|
'links' => array(), |
101
|
|
|
); |
102
|
|
|
|
103
|
|
|
foreach ($this->enum_domains() as $d) { |
104
|
|
|
$obj['links'][] = array( |
105
|
|
|
'txt' => htmlentities($d["domaine"]) . ' ' . htmlentities("(" . $d["nb_mail"] . ")"), |
106
|
|
|
'url' => "mail_list.php?domain_id=" . urlencode($d['id']), |
107
|
|
|
); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
return $obj; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
function get_total_size_for_domain($domain) { |
114
|
|
|
global $db; |
115
|
|
|
if (empty($this->cache_domain_mail_size)) { |
116
|
|
|
$db->query("SELECT SUBSTRING_INDEX(user,'@', -1) as domain, SUM(quota_dovecot) AS sum FROM dovecot_view group by domain ;"); |
117
|
|
|
while ($db->next_record()) { |
118
|
|
|
$dd = $db->f('domain'); |
119
|
|
|
$this->cache_domain_mail_size[$dd] = $db->f('sum'); |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
if (isset($this->cache_domain_mail_size[$domain])) { |
123
|
|
|
return $this->cache_domain_mail_size[$domain]; |
124
|
|
|
} |
125
|
|
|
return 0; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// FIXME documenter |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param string $domain_id |
132
|
|
|
*/ |
133
|
|
|
function catchall_getinfos($domain_id) { |
134
|
|
|
global $dom, $db; |
135
|
|
|
$rr = array( |
136
|
|
|
'mail_id' => '', |
137
|
|
|
'domain' => $dom->get_domain_byid($domain_id), |
138
|
|
|
'target' => '', |
139
|
|
|
'type' => '', |
140
|
|
|
); |
141
|
|
|
|
142
|
|
|
$db->query("select r.recipients as dst, a.id mail_id from address a, recipient r where a.domain_id = ? and r.address_id = a.id and a.address='';", array($domain_id)); |
143
|
|
|
if ($db->next_record()) { |
144
|
|
|
$rr['target'] = $db->f('dst'); |
145
|
|
|
$rr['mail_id'] = $db->f('mail_id'); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
// Does it redirect to a specific mail or to a domain |
149
|
|
|
if (empty($rr['target'])) { |
150
|
|
|
$rr['type'] = 'none'; |
151
|
|
|
} elseif (substr($rr['target'], 0, 1) == '@') { |
152
|
|
|
$rr['type'] = 'domain'; |
153
|
|
|
} else { |
154
|
|
|
$rr['type'] = 'mail'; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
return $rr; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @param string $domain_id |
162
|
|
|
*/ |
163
|
|
|
function catchall_del($domain_id) { |
164
|
|
|
$catch = $this->catchall_getinfos($domain_id); |
165
|
|
|
if (empty($catch['mail_id'])) { |
166
|
|
|
return false; |
167
|
|
|
} |
168
|
|
|
return $this->delete($catch['mail_id']); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @param string $domain_id |
173
|
|
|
* @param string $target |
174
|
|
|
*/ |
175
|
|
|
function catchall_set($domain_id, $target) { |
176
|
|
|
global $err; |
177
|
|
|
$target = rtrim($target); |
178
|
|
|
if (substr_count($target, '@') == 0) { // Pas de @ |
179
|
|
|
$target = '@' . $target; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
if (substr($target, 0, 1) == '@') { // le premier caractere est un @ |
183
|
|
|
// FIXME validate domain |
184
|
|
|
} else { // ca doit être un mail |
185
|
|
|
if (!filter_var($target, FILTER_VALIDATE_EMAIL)) { |
186
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
187
|
|
|
return false; |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
$this->catchall_del($domain_id); |
191
|
|
|
$err->error = ""; |
192
|
|
|
return $this->create_alias($domain_id, '', $target, "catchall", true); |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/* ----------------------------------------------------------------- */ |
196
|
|
|
|
197
|
|
|
/** get_quota (hook for quota class), returns the number of used |
198
|
|
|
* service for a quota-bound service |
199
|
|
|
* @param $name string the named quota we want |
200
|
|
|
* @return the number of used service for the specified quota, |
201
|
|
|
* or false if I'm not the one for the named quota |
202
|
|
|
*/ |
203
|
|
|
function hook_quota_get() { |
204
|
|
|
global $db, $err, $cuid; |
205
|
|
|
$err->log("mail", "getquota"); |
206
|
|
|
$q = Array("name" => "mail", "description" => _("Email addresses"), "used" => 0); |
207
|
|
|
$db->query("SELECT COUNT(*) AS cnt FROM address a, domaines d WHERE a.domain_id=d.id AND d.compte= ? AND a.type='';", array($cuid)); |
208
|
|
|
if ($db->next_record()) { |
209
|
|
|
$q['used'] = $db->f("cnt"); |
210
|
|
|
} |
211
|
|
|
return $q; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/* ----------------------------------------------------------------- */ |
215
|
|
|
|
216
|
|
|
/** Password policy kind used in this class (hook for admin class) |
217
|
|
|
* @return array an array of policykey => "policy name (for humans)" |
218
|
|
|
*/ |
219
|
|
|
function alternc_password_policy() { |
220
|
|
|
return array("pop" => _("Email account password")); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/* ----------------------------------------------------------------- */ |
224
|
|
|
|
225
|
|
|
/** Returns the list of mail-hosting domains for a user |
226
|
|
|
* @return array indexed array of hosted domains |
227
|
|
|
*/ |
228
|
|
|
function enum_domains($uid = -1) { |
229
|
|
|
global $db, $err, $cuid; |
230
|
|
|
$err->log("mail", "enum_domains"); |
231
|
|
|
if ($uid == -1) { |
232
|
|
|
$uid = $cuid; |
233
|
|
|
} |
234
|
|
|
$db->query(" |
235
|
|
|
SELECT |
236
|
|
|
d.id, |
237
|
|
|
d.domaine, |
238
|
|
|
IFNULL( COUNT(a.id), 0) as nb_mail |
239
|
|
|
FROM |
240
|
|
|
domaines d LEFT JOIN address a ON (d.id=a.domain_id AND a.type='') |
241
|
|
|
WHERE |
242
|
|
|
d.compte = ? |
243
|
|
|
and d.gesmx = 1 |
244
|
|
|
GROUP BY |
245
|
|
|
d.id |
246
|
|
|
ORDER BY |
247
|
|
|
d.domaine |
248
|
|
|
; |
249
|
|
|
", array($uid)); |
250
|
|
|
$this->enum_domains = array(); |
251
|
|
|
while ($db->next_record()) { |
252
|
|
|
$this->enum_domains[] = $db->Record; |
253
|
|
|
} |
254
|
|
|
return $this->enum_domains; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/* ----------------------------------------------------------------- */ |
258
|
|
|
|
259
|
|
|
/** available: tells if an email address can be installed in the server |
260
|
|
|
* check the domain part (is it mine too), the syntax, and the availability. |
261
|
|
|
* @param $mail string email to check |
262
|
|
|
* @return boolean true if the email can be installed on the server |
263
|
|
|
*/ |
264
|
|
|
function available($mail) { |
265
|
|
|
global $db, $err, $dom; |
266
|
|
|
$err->log("mail", "available"); |
267
|
|
|
list($login, $domain) = explode("@", $mail, 2); |
268
|
|
|
// Validate the domain ownership & syntax |
269
|
|
|
if (!($dom_id = $dom->get_domain_byname($domain))) { |
270
|
|
|
return false; |
271
|
|
|
} |
272
|
|
|
// Validate the email syntax: |
273
|
|
|
if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) { |
274
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
275
|
|
|
return false; |
276
|
|
|
} |
277
|
|
|
// Check the availability |
278
|
|
|
$db->query("SELECT a.id FROM address a WHERE a.domain_id= ? AND a.address= ?;", array($dom_id, $login)); |
279
|
|
|
if ($db->next_record()) { |
280
|
|
|
return false; |
281
|
|
|
} else { |
282
|
|
|
return true; |
283
|
|
|
} |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/* ----------------------------------------------------------------- */ |
287
|
|
|
/* function used to list every mail address hosted on a domain. |
288
|
|
|
* @param $dom_id integer the domain id. |
289
|
|
|
* @param $search string search that string in recipients or address. |
290
|
|
|
* @param $offset integer skip THAT much emails in the result. |
291
|
|
|
* @param $count integer return no more than THAT much emails. -1 for ALL. Offset is ignored then. |
292
|
|
|
* @result an array of each mail hosted under the domain. |
293
|
|
|
*/ |
294
|
|
|
|
295
|
|
|
function enum_domain_mails($dom_id = null, $search = "", $offset = 0, $count = 30, $show_systemmails = false) { |
296
|
|
|
global $db, $err, $hooks; |
297
|
|
|
$err->log("mail", "enum_domains_mail"); |
298
|
|
|
|
299
|
|
|
$query_args = array($dom_id); |
300
|
|
|
$search = trim($search); |
301
|
|
|
$where = " a.domain_id = ? "; |
302
|
|
|
|
303
|
|
|
if ($search) { |
304
|
|
|
$where .= " AND (a.address LIKE ? OR r.recipients LIKE ? )"; |
305
|
|
|
array_push($query_args, "%" . $search . "%", "%" . $search . "%"); |
306
|
|
|
} |
307
|
|
|
if (!$show_systemmails) { |
308
|
|
|
$where .= " AND type='' "; |
309
|
|
|
} |
310
|
|
|
$db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE " . $where . ";", $query_args); |
311
|
|
|
$db->next_record(); |
312
|
|
|
$this->total = $db->f("total"); |
313
|
|
|
if ($count != -1) { |
314
|
|
|
$offset = intval($offset); |
315
|
|
|
$count = intval($count); |
316
|
|
|
$limit = " LIMIT $offset, $count "; |
317
|
|
|
} else { |
318
|
|
|
$limit = ""; |
319
|
|
|
} |
320
|
|
|
$db->query("SELECT a.id, a.address, a.password, a.`enabled`, a.mail_action, d.domaine AS domain, m.quota, m.quota*1024*1024 AS quotabytes, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.domain_id |
321
|
|
|
FROM (address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN recipient r ON r.address_id=a.id, domaines d |
322
|
|
|
WHERE " . $where . " AND d.id=a.domain_id " . $limit . " ;", $query_args); |
323
|
|
|
if (!$db->next_record()) { |
324
|
|
|
$err->raise("mail", _("No email found for this query")); |
325
|
|
|
return array(); |
326
|
|
|
} |
327
|
|
|
$res = array(); |
328
|
|
|
do { |
329
|
|
|
$details = $db->Record; |
330
|
|
|
// if necessary, fill the typedata with data from hooks ... |
331
|
|
|
if ($details["type"]) { |
332
|
|
|
$result = $hooks->invoke("hook_mail_get_details", array($details)); // Will fill typedata if necessary |
333
|
|
|
$details["typedata"] = implode("<br />", $result); |
334
|
|
|
} |
335
|
|
|
$res[] = $details; |
336
|
|
|
} while ($db->next_record()); |
337
|
|
|
return $res; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
function hook_mail_get_details($detail) { |
341
|
|
|
if ($detail['type'] == 'catchall') { |
342
|
|
|
return _(sprintf("Special mail address for catch-all. <a href='mail_manage_catchall.php?domain_id=%s'>Click here to manage it.</a>", $detail['domain_id'])); |
343
|
|
|
} |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/* ----------------------------------------------------------------- */ |
347
|
|
|
|
348
|
|
|
/** Function used to insert a new mail into the db |
349
|
|
|
* should be used by the web interface, not by third-party programs. |
350
|
|
|
* |
351
|
|
|
* This function calls the hook "hooks_mail_cancreate" |
352
|
|
|
* which must return FALSE if the user can't create this email, and raise and error accordingly |
353
|
|
|
* |
354
|
|
|
* @param $dom_id integer A domain_id (owned by the user) |
355
|
|
|
* (will be the part at the right of the @ in the email) |
356
|
|
|
* @param $mail string the left part of the email to create (something@dom_id) |
357
|
|
|
* @return an hashtable containing the database id of the newly created mail, |
358
|
|
|
* or false if an error occured ($err is filled accordingly) |
359
|
|
|
*/ |
360
|
|
|
function create($dom_id, $mail, $type = "", $dontcheck = false) { |
361
|
|
|
global $err, $db, $quota, $dom, $hooks; |
362
|
|
|
$err->log("mail", "create", $mail); |
363
|
|
|
|
364
|
|
|
// Validate the domain id |
365
|
|
|
if (!($domain = $dom->get_domain_byid($dom_id))) { |
366
|
|
|
return false; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
// Validate the email syntax: |
370
|
|
|
$m = $mail . "@" . $domain; |
371
|
|
|
if (!filter_var($m, FILTER_VALIDATE_EMAIL) && !$dontcheck) { |
372
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
373
|
|
|
return false; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
// Call other classes to check we can create it: |
377
|
|
|
$cancreate = $hooks->invoke("hook_mail_cancreate", array($dom_id, $mail)); |
378
|
|
|
if (in_array(false, $cancreate, true)) { |
379
|
|
|
return false; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
// Check the quota: |
383
|
|
|
if (($type=="")&&!$quota->cancreate("mail")) { |
384
|
|
|
$err->raise("mail", _("You cannot create email addresses: your quota is over")); |
385
|
|
|
return false; |
386
|
|
|
} |
387
|
|
|
// Already exists? |
388
|
|
|
$db->query("SELECT * FROM address WHERE domain_id= ? AND address= ? ;", array($dom_id, $mail)); |
389
|
|
|
if ($db->next_record()) { |
390
|
|
|
$err->raise("mail", _("This email address already exists")); |
391
|
|
|
return false; |
392
|
|
|
} |
393
|
|
|
// Create it now |
394
|
|
|
$db->query("INSERT INTO address (domain_id, address,type) VALUES (?, ?, ?);", array($dom_id, $mail, $type)); |
395
|
|
|
if (!($id = $db->lastid())) { |
396
|
|
|
$err->raise("mail", _("An unexpected error occured when creating the email")); |
397
|
|
|
return false; |
398
|
|
|
} |
399
|
|
|
return $id; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/* ----------------------------------------------------------------- */ |
403
|
|
|
|
404
|
|
|
/** function used to get every information we can on a mail |
405
|
|
|
* @param $mail_id integer |
406
|
|
|
* @return array a hashtable with all the informations for that email |
407
|
|
|
*/ |
408
|
|
|
function get_details($mail_id) { |
409
|
|
|
global $db, $err, $hooks; |
410
|
|
|
$err->log("mail", "get_details"); |
411
|
|
|
|
412
|
|
|
$mail_id = intval($mail_id); |
413
|
|
|
// Validate that this email is owned by me... |
414
|
|
|
if (!($mail = $this->is_it_my_mail($mail_id))) { |
415
|
|
|
return false; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
// We fetch all the informations for that email: these will fill the hastable : |
419
|
|
|
$db->query("SELECT a.id, a.address, a.password, a.enabled, d.domaine AS domain, m.path, m.quota, m.quota*1024*1024 AS quotabytes, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin, a.mail_action, m.mail_action AS mailbox_action FROM (address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN recipient r ON r.address_id=a.id, domaines d WHERE a.id= ? AND d.id=a.domain_id;", array($mail_id)); |
420
|
|
|
if (!$db->next_record()) { |
421
|
|
|
return false; |
422
|
|
|
} |
423
|
|
|
$details = $db->Record; |
424
|
|
|
// if necessary, fill the typedata with data from hooks ... |
425
|
|
|
if ($details["type"]) { |
426
|
|
|
$result = $hooks->invoke("hook_mail_get_details", array($mail_id)); // Will fill typedata if necessary |
427
|
|
|
$details["typedata"] = implode("<br />", $result); |
428
|
|
|
} |
429
|
|
|
return $details; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
private $isitmy_cache = array(); |
433
|
|
|
|
434
|
|
|
/* ----------------------------------------------------------------- */ |
435
|
|
|
|
436
|
|
|
/** Check if an email is mine ... |
437
|
|
|
* |
438
|
|
|
* @param $mail_id integer the number of the email to check |
439
|
|
|
* @return string the complete email address if that's mine, false if not |
440
|
|
|
* ($err is filled accordingly) |
441
|
|
|
*/ |
442
|
|
|
function is_it_my_mail($mail_id) { |
443
|
|
|
global $err, $db, $cuid; |
444
|
|
|
$mail_id = intval($mail_id); |
445
|
|
|
// cache it (may be called more than one time in the same page). |
446
|
|
|
if (isset($this->isitmy_cache[$mail_id])) { |
447
|
|
|
return $this->isitmy_cache[$mail_id]; |
448
|
|
|
} |
449
|
|
|
$db->query("SELECT concat(a.address,'@',d.domaine) AS email FROM address a, domaines d WHERE d.id=a.domain_id AND a.id= ? AND d.compte= ?;", array($mail_id, $cuid)); |
450
|
|
|
if ($db->next_record()) { |
451
|
|
|
return $this->isitmy_cache[$mail_id] = $db->f("email"); |
452
|
|
|
} else { |
453
|
|
|
$err->raise("mail", _("This email is not yours, you can't change anything on it")); |
454
|
|
|
return $this->isitmy_cache[$mail_id] = false; |
455
|
|
|
} |
456
|
|
|
} |
457
|
|
|
|
458
|
|
|
/* ----------------------------------------------------------------- */ |
459
|
|
|
|
460
|
|
|
/** Hook called when the DOMAIN class will delete a domain. |
461
|
|
|
* OR when the DOMAIN class tells us we don't host the emails of this domain anymore. |
462
|
|
|
* @param $dom the ID of the domain to delete |
463
|
|
|
* @return boolean if the email has been properly deleted |
464
|
|
|
* or false if an error occured ($err is filled accordingly) |
465
|
|
|
*/ |
466
|
|
|
function hook_dom_del_mx_domain($dom_id) { |
467
|
|
|
global $db; |
468
|
|
|
$list = $this->enum_domain_mails($dom_id, "", 0, -1); |
469
|
|
|
if (is_array($list)) { |
470
|
|
|
foreach ($list as $one) { |
471
|
|
|
$this->delete($one["id"]); |
472
|
|
|
} |
473
|
|
|
} |
474
|
|
|
$db->query("SELECT domaine FROM domaines WHERE id= ? ;", array($dom_id)); |
475
|
|
|
if ($db->next_record()) { |
476
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND type='txt' AND (sub='' AND valeur LIKE 'v=spf1 %') OR (sub='_dmarc' AND valeur LIKE 'v=dmarc1;%');", array($db->Record["domaine"])); |
477
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine= ? AND (type='defmx' OR type='defmx2');", array($db->Record["domaine"])); |
478
|
|
|
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id= ? ;", array($dom_id)); |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
return true; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
// return the alternc account's ID of the mail_id |
485
|
|
|
function get_account_by_mail_id($mail_id) { |
486
|
|
|
global $db; |
487
|
|
|
$db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = ? ;", array($mail_id)); |
488
|
|
|
if (!$db->next_record()) { |
489
|
|
|
return false; |
490
|
|
|
} |
491
|
|
|
return $db->f('uid'); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/* ----------------------------------------------------------------- */ |
495
|
|
|
|
496
|
|
|
/** Function used to delete a mail from the db |
497
|
|
|
* should be used by the web interface, not by third-party programs. |
498
|
|
|
* |
499
|
|
|
* @param $mail_id integer the number of the email to delete |
500
|
|
|
* @return boolean if the email has been properly deleted |
501
|
|
|
* or false if an error occured ($err is filled accordingly) |
502
|
|
|
*/ |
503
|
|
|
function delete($mail_id) { |
504
|
|
|
global $err, $db, $hooks; |
505
|
|
|
$err->log("mail", "delete"); |
506
|
|
|
|
507
|
|
|
$mail_id = intval($mail_id); |
508
|
|
|
|
509
|
|
|
if (!$mail_id) { |
510
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
511
|
|
|
return false; |
512
|
|
|
} |
513
|
|
|
// Validate that this email is owned by me... |
514
|
|
|
if (!($mail = $this->is_it_my_mail($mail_id))) { |
515
|
|
|
return false; |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
$mailinfos = $this->get_details($mail_id); |
519
|
|
|
$hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'] . '@' . $mailinfos['domain'])); |
520
|
|
|
|
521
|
|
|
// Search for that address: |
522
|
|
|
$db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id= ? ;", array($mail_id)); |
523
|
|
|
if (!$db->next_record()) { |
524
|
|
|
$err->raise("mail", _("The email %s does not exist, it can't be deleted"), $mail); |
525
|
|
|
return false; |
526
|
|
|
} |
527
|
|
|
if ($db->f("mail_action") != "OK" || ($db->f("islocal") && $db->f("mailbox_action") != "OK")) { // will be deleted soon ... |
528
|
|
|
$err->raise("mail", _("The email %s is already marked for deletion, it can't be deleted"), $mail); |
529
|
|
|
return false; |
530
|
|
|
} |
531
|
|
|
$mail_id = $db->f("id"); |
532
|
|
|
|
533
|
|
|
if ($db->f("islocal")) { |
534
|
|
|
// If it's a pop/imap mailbox, mark it for deletion |
535
|
|
|
$db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id= ?;", array($mail_id)); |
536
|
|
|
$db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id= ?;", array($mail_id)); |
537
|
|
|
$err->raise("mail", _("The email %s has been marked for deletion"), $mail); |
538
|
|
|
} else { |
539
|
|
|
// If it's only aliases, delete it NOW. |
540
|
|
|
$db->query("DELETE FROM address WHERE id= ? ;", array($mail_id)); |
541
|
|
|
$db->query("DELETE FROM mailbox WHERE address_id= ? ;", array($mail_id)); |
542
|
|
|
$db->query("DELETE FROM recipient WHERE address_id= ? ;", array($mail_id)); |
543
|
|
|
$err->raise("mail", _("The email %s has been successfully deleted"), $mail); |
544
|
|
|
} |
545
|
|
|
return true; |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/* ----------------------------------------------------------------- */ |
549
|
|
|
|
550
|
|
|
/** Function used to undelete a pending deletion mail from the db |
551
|
|
|
* should be used by the web interface, not by third-party programs. |
552
|
|
|
* |
553
|
|
|
* @param $mail_id integer the email id |
554
|
|
|
* @return boolean if the email has been properly undeleted |
555
|
|
|
* or false if an error occured ($err is filled accordingly) |
556
|
|
|
*/ |
557
|
|
|
function undelete($mail_id) { |
558
|
|
|
global $err, $db; |
559
|
|
|
$err->log("mail", "undelete"); |
560
|
|
|
|
561
|
|
|
$mail_id = intval($mail_id); |
562
|
|
|
|
563
|
|
|
if (!$mail_id) { |
564
|
|
|
$err->raise("mail", _("The email you entered does not exist")); |
565
|
|
|
return false; |
566
|
|
|
} |
567
|
|
|
// Validate that this email is owned by me... |
568
|
|
|
if (!($mail = $this->is_it_my_mail($mail_id))) { |
569
|
|
|
return false; |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
// Search for that address: |
573
|
|
|
$db->query("SELECT a.id, a.type, a.mail_action, m.mail_action AS mailbox_action, NOT ISNULL(m.id) AS islocal FROM address a LEFT JOIN mailbox m ON m.address_id=a.id WHERE a.id= ? ;", array($mail_id)); |
574
|
|
|
if (!$db->next_record()) { |
575
|
|
|
$err->raise("mail", _("The email %s does not exist, it can't be undeleted"), $mail); |
576
|
|
|
return false; |
577
|
|
|
} |
578
|
|
|
if ($db->f("type") != "") { // Technically special : mailman, sympa ... |
579
|
|
|
$err->raise("mail", _("The email %s is special, it can't be undeleted"), $mail); |
580
|
|
|
return false; |
581
|
|
|
} |
582
|
|
|
if ($db->f("mailbox_action") != "DELETE" || $db->f("mail_action") != "DELETE") { // will be deleted soon ... |
583
|
|
|
$err->raise("mail", _("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"), $mail); |
584
|
|
|
return false; |
585
|
|
|
} |
586
|
|
|
$mail_id = $db->f("id"); |
587
|
|
|
|
588
|
|
|
if ($db->f("islocal")) { |
589
|
|
|
// If it's a pop/imap mailbox, mark it for deletion |
590
|
|
|
$db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id= ?;", array($mail_id)); |
591
|
|
|
$db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id= ? ;", array($mail_id)); |
592
|
|
|
$err->raise("mail", _("The email %s has been undeleted"), $mail); |
593
|
|
|
return true; |
594
|
|
|
} else { |
595
|
|
|
$err->raise("mail", _("-- Program Error -- The email %s can't be undeleted"), $mail); |
596
|
|
|
return false; |
597
|
|
|
} |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/* ----------------------------------------------------------------- */ |
601
|
|
|
|
602
|
|
|
/** set the password of an email address. |
603
|
|
|
* @param $mail_id integer email ID |
604
|
|
|
* @param $pass string the new password. |
605
|
|
|
* @return boolean true if the password has been set, false else, raise an error. |
606
|
|
|
*/ |
607
|
|
|
function set_passwd($mail_id, $pass) { |
608
|
|
|
global $db, $err, $admin; |
609
|
|
|
$err->log("mail", "setpasswd"); |
610
|
|
|
|
611
|
|
|
if (!($email = $this->is_it_my_mail($mail_id))) { |
612
|
|
|
return false; |
613
|
|
|
} |
614
|
|
|
if (!$admin->checkPolicy("pop", $email, $pass)) { |
615
|
|
|
return false; |
616
|
|
|
} |
617
|
|
|
if (!$db->query("UPDATE address SET password= ? where id = ? ;", array(_md5cr($pass), $mail_id ))) { |
618
|
|
|
return false; |
619
|
|
|
} |
620
|
|
|
return true; |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
/* ----------------------------------------------------------------- */ |
624
|
|
|
|
625
|
|
|
/** Enables an email address. |
626
|
|
|
* @param $mail_id integer Email ID |
627
|
|
|
* @return boolean true if the email has been enabled. |
628
|
|
|
*/ |
629
|
|
|
function enable($mail_id) { |
630
|
|
|
global $db, $err; |
631
|
|
|
$err->log("mail", "enable"); |
632
|
|
|
if (!($email = $this->is_it_my_mail($mail_id))) { |
633
|
|
|
return false; |
634
|
|
|
} |
635
|
|
|
if (!$db->query("UPDATE address SET `enabled`=1 where id= ? ;", array($mail_id))) { |
636
|
|
|
return false; |
637
|
|
|
} |
638
|
|
|
return true; |
639
|
|
|
} |
640
|
|
|
|
641
|
|
|
/* ----------------------------------------------------------------- */ |
642
|
|
|
|
643
|
|
|
/** Disables an email address. |
644
|
|
|
* @param $mail_id integer Email ID |
645
|
|
|
* @return boolean true if the email has been enabled. |
646
|
|
|
*/ |
647
|
|
|
function disable($mail_id) { |
648
|
|
|
global $db, $err; |
649
|
|
|
$err->log("mail", "disable"); |
650
|
|
|
if (!($email = $this->is_it_my_mail($mail_id))) { |
651
|
|
|
return false; |
652
|
|
|
} |
653
|
|
|
if (!$db->query("UPDATE address SET `enabled`=0 where id= ? ;", array($mail_id))) { |
654
|
|
|
return false; |
655
|
|
|
} |
656
|
|
|
return true; |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
/* ----------------------------------------------------------------- */ |
660
|
|
|
|
661
|
|
|
/** Function used to update an email settings |
662
|
|
|
* should be used by the web interface, not by third-party programs. |
663
|
|
|
* |
664
|
|
|
* @param $mail_id integer the number of the email to delete |
665
|
|
|
* @param integer $islocal boolean is it a POP/IMAP mailbox ? |
666
|
|
|
* @param integer $quotamb integer if islocal=1, quota in MB |
667
|
|
|
* @param string $recipients string recipients, one mail per line. |
668
|
|
|
* @return boolean if the email has been properly edited |
669
|
|
|
* or false if an error occured ($err is filled accordingly) |
670
|
|
|
*/ |
671
|
|
|
function set_details($mail_id, $islocal, $quotamb, $recipients, $delivery = "dovecot", $dontcheck = false) { |
672
|
|
|
global $err, $db; |
673
|
|
|
$err->log("mail", "set_details"); |
674
|
|
|
if (!($me = $this->get_details($mail_id))) { |
675
|
|
|
return false; |
676
|
|
|
} |
677
|
|
|
if ($me["islocal"] && !$islocal) { |
678
|
|
|
// delete pop |
679
|
|
|
$db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id= ? ;", array($mail_id)); |
680
|
|
|
} |
681
|
|
|
if (!$me["islocal"] && $islocal) { |
682
|
|
|
// create pop |
683
|
|
|
$path = ""; |
684
|
|
|
if ($delivery == "dovecot") { |
685
|
|
|
$path = ALTERNC_MAIL . "/" . substr($me["address"] . "_", 0, 1) . "/" . $me["address"] . "_" . $me["domain"]; |
686
|
|
|
} |
687
|
|
|
foreach ($this->forbiddenchars as $str) { |
688
|
|
|
if (strpos($me["address"], $str) !== false) { |
689
|
|
|
$err->raise("mail", _("There is forbidden characters in your email address. You can't make it a POP/IMAP account, you can only use it as redirection to other emails")); |
690
|
|
|
return false; |
691
|
|
|
} |
692
|
|
|
} |
693
|
|
|
foreach ($this->specialchars as $str) { |
694
|
|
|
if (strpos($me["address"], $str) !== false) { |
695
|
|
|
$path = ALTERNC_MAIL . "/_/" . $me["id"] . "_" . $me["domain"]; |
696
|
|
|
break; |
697
|
|
|
} |
698
|
|
|
} |
699
|
|
|
$db->query("INSERT INTO mailbox SET address_id= ? , delivery= ?, path= ? ;", array($mail_id, $delivery, $path)); |
700
|
|
|
} |
701
|
|
|
if ($me["islocal"] && $islocal && $me["mailbox_action"] == "DELETE") { |
702
|
|
|
$db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id= ? ;", array($mail_id)); |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
if ($islocal) { |
706
|
|
|
if ($quotamb != 0 && $quotamb < (intval($me["used"] / 1024 / 1024) + 1)) { |
707
|
|
|
$quotamb = intval($me["used"] / 1024 / 1024) + 1; |
708
|
|
|
$err->raise("mail", _("You set a quota smaller than the current mailbox size. Since it's not allowed, we set the quota to the current mailbox size")); |
709
|
|
|
} |
710
|
|
|
$db->query("UPDATE mailbox SET quota= ? WHERE address_id= ? ;", array($quotamb, $mail_id)); |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
$recipients = preg_replace('/[\r\t\s]/', "\n", $recipients); // Handle space AND new line |
714
|
|
|
$r = explode("\n", $recipients); |
715
|
|
|
$red = ""; |
716
|
|
|
foreach ($r as $m) { |
717
|
|
|
$m = trim($m); |
718
|
|
|
if ($m && ( filter_var($m, FILTER_VALIDATE_EMAIL) || $dontcheck) // Recipient Email is valid |
719
|
|
|
&& $m != ($me["address"] . "@" . $me["domain"])) { // And not myself (no loop allowed easily ;) ) |
720
|
|
|
$red.=$m . "\n"; |
721
|
|
|
} |
722
|
|
|
} |
723
|
|
|
$db->query("DELETE FROM recipient WHERE address_id= ? ;", array($mail_id)); |
724
|
|
|
if (isset($red) && $red) { |
725
|
|
|
$db->query("INSERT INTO recipient SET address_id= ?, recipients= ? ;", array($mail_id, $red)); |
726
|
|
|
} |
727
|
|
|
if (!$islocal && !$red) { |
|
|
|
|
728
|
|
|
$err->raise("mail", _("Warning: you created an email which is not an alias, and not a POP/IMAP mailbox. This is certainly NOT what you want to do. To fix this, edit the email address and check 'Yes' in POP/IMAP account, or set some recipients in the redirection field.")); |
729
|
|
|
} |
730
|
|
|
return true; |
731
|
|
|
} |
732
|
|
|
|
733
|
|
|
/* ----------------------------------------------------------------- */ |
734
|
|
|
|
735
|
|
|
/** A wrapper used by mailman class to create it's needed addresses |
736
|
|
|
* @ param : $dom_id , the domain id associated to a given address |
737
|
|
|
* @ param : $m , the left part of the mail address being created |
738
|
|
|
* @ param : $delivery , the delivery used to deliver the mail |
739
|
|
|
*/ |
740
|
|
|
function add_wrapper($dom_id, $m, $delivery) { |
741
|
|
|
global $err, $mail; |
742
|
|
|
$err->log("mail", "add_wrapper", "creating $delivery $m address"); |
743
|
|
|
|
744
|
|
|
$mail_id = $mail->create($dom_id, $m, $delivery); |
745
|
|
|
$this->set_details($mail_id, 1, 0, '', $delivery); |
746
|
|
|
// FIXME return error code |
747
|
|
|
} |
748
|
|
|
|
749
|
|
|
/* ----------------------------------------------------------------- */ |
750
|
|
|
|
751
|
|
|
/** A function used to create an alias for a specific address |
752
|
|
|
* @ param : $dom_id , the domain sql identifier |
753
|
|
|
* @ param : $m , the alias we want to create |
754
|
|
|
* @ param : $alias , the already existing aliased address |
755
|
|
|
* @ param : $type, the type of the alias created |
756
|
|
|
* @param string $m |
757
|
|
|
* @param string $alias |
758
|
|
|
* @param string $dom_id |
759
|
|
|
*/ |
760
|
|
|
function create_alias($dom_id, $m, $alias, $type = "", $dontcheck = false) { |
761
|
|
|
global $err, $mail; |
762
|
|
|
$err->log("mail", "create_alias", "creating $m alias for $alias type $type"); |
763
|
|
|
|
764
|
|
|
$mail_id = $mail->create($dom_id, $m, $type, $dontcheck); |
765
|
|
|
if (!$mail_id) { |
766
|
|
|
return false; |
767
|
|
|
} |
768
|
|
|
$this->set_details($mail_id, 0, 0, $alias, "dovecot", $dontcheck); |
769
|
|
|
return true; |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
/* ----------------------------------------------------------------- */ |
773
|
|
|
|
774
|
|
|
/** A wrapper used by mailman class to create it's needed addresses |
775
|
|
|
* @ param : $mail_id , the mysql id of the mail address we want to delete |
776
|
|
|
* of the email for the current acccount. |
777
|
|
|
*/ |
778
|
|
|
function del_wrapper($mail_id) { |
779
|
|
|
global $err; |
780
|
|
|
$err->log("mail", "del_wrapper"); |
781
|
|
|
$this->delete($mail_id); |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
/* ----------------------------------------------------------------- */ |
785
|
|
|
|
786
|
|
|
/** Export the mail information of an account |
787
|
|
|
* @return: str, string containing the complete configuration |
788
|
|
|
* of the email for the current acccount. |
789
|
|
|
*/ |
790
|
|
|
function alternc_export_conf() { |
791
|
|
|
global $err; |
792
|
|
|
$err->log("mail", "export"); |
793
|
|
|
$domain = $this->enum_domains(); |
794
|
|
|
$str = "<mail>\n"; |
795
|
|
|
foreach ($domain as $d) { |
796
|
|
|
$str.=" <domain>\n <name>" . xml_entities($d["domain"]) . "</name>\n"; |
797
|
|
|
$s = $this->enum_domain_mails($d["id"]); |
798
|
|
|
if (count($s)) { |
799
|
|
|
while (list($key, $val) = each($s)) { |
800
|
|
|
$str.=" <address>\n"; |
801
|
|
|
$str.=" <name>" . xml_entities($val["address"]) . "</name>\n"; |
802
|
|
|
$str.=" <enabled>" . xml_entities($val["enabled"]) . "</enabled>\n"; |
803
|
|
|
if (is_array($val["islocal"])) { |
804
|
|
|
$str.=" <islocal>1</islocal>\n"; |
805
|
|
|
$str.=" <quota>" . $val["quota"] . "</quota>\n"; |
806
|
|
|
$str.=" <path>" . $val["path"] . "</path>\n"; |
807
|
|
|
} else { |
808
|
|
|
$str.=" <islocal>0</islocal>\n"; |
809
|
|
|
} |
810
|
|
|
if (!empty($val["recipients"])) { |
811
|
|
|
$r = explode("\n", $val["recipients"]); |
812
|
|
|
foreach ($r as $recip) { |
813
|
|
|
$str.=" <recipients>" . $recip . "<recipients>\n"; |
814
|
|
|
} |
815
|
|
|
} |
816
|
|
|
$str.=" </address>\n"; |
817
|
|
|
} |
818
|
|
|
} |
819
|
|
|
$str.=" </domain>\n"; |
820
|
|
|
} |
821
|
|
|
$str.="</mail>\n"; |
822
|
|
|
return $str; |
823
|
|
|
} |
824
|
|
|
|
825
|
|
|
/* ----------------------------------------------------------------- */ |
826
|
|
|
|
827
|
|
|
/** |
828
|
|
|
* Return the list of allowed slave accounts (secondary-mx) |
829
|
|
|
* @return array |
830
|
|
|
*/ |
831
|
|
|
function enum_slave_account() { |
832
|
|
|
global $db; |
833
|
|
|
$db->query("SELECT login,pass FROM mxaccount;"); |
834
|
|
|
$res = array(); |
835
|
|
|
while ($db->next_record()) { |
836
|
|
|
$res[] = $db->Record; |
837
|
|
|
} |
838
|
|
|
if (!count($res)) { |
839
|
|
|
return false; |
840
|
|
|
} |
841
|
|
|
return $res; |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
/* ----------------------------------------------------------------- */ |
845
|
|
|
|
846
|
|
|
/** |
847
|
|
|
* Check for a slave account (secondary mx) |
848
|
|
|
* @param string $login the login to check |
849
|
|
|
* @param string $pass the password to check |
850
|
|
|
* @return boolean TRUE if the password is correct, or FALSE if an error occurred. |
851
|
|
|
*/ |
852
|
|
|
function check_slave_account($login, $pass) { |
853
|
|
|
global $db; |
854
|
|
|
$db->query("SELECT * FROM mxaccount WHERE login= ? AND pass= ?;", array($login, $pass)); |
855
|
|
|
if ($db->next_record()) { |
856
|
|
|
return true; |
857
|
|
|
} |
858
|
|
|
return false; |
859
|
|
|
} |
860
|
|
|
|
861
|
|
|
/* ----------------------------------------------------------------- */ |
862
|
|
|
|
863
|
|
|
/** Out (echo) the complete hosted domain list : |
864
|
|
|
*/ |
865
|
|
|
function echo_domain_list($format = null) { |
866
|
|
|
global $db; |
867
|
|
|
$db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine"); |
868
|
|
|
$lst = array(); |
869
|
|
|
$tt = ""; |
870
|
|
|
while ($db->next_record()) { |
871
|
|
|
$lst[] = $db->f("domaine"); |
872
|
|
|
$tt.=$db->f("domaine"); |
873
|
|
|
} |
874
|
|
|
|
875
|
|
|
# Generate an integrity check |
876
|
|
|
$obj = array('integrity' => md5($tt), 'items' => $lst); |
877
|
|
|
|
878
|
|
|
switch ($format) { |
879
|
|
|
case "json": |
880
|
|
|
return json_encode($obj); |
881
|
|
|
default: |
882
|
|
|
foreach ($lst as $l) { |
883
|
|
|
echo $l . "\n"; |
884
|
|
|
} |
885
|
|
|
return true; |
886
|
|
|
} // switch |
887
|
|
|
} |
888
|
|
|
|
889
|
|
|
/* ----------------------------------------------------------------- */ |
890
|
|
|
|
891
|
|
|
/** |
892
|
|
|
* Add a slave account that will be allowed to access the mxdomain list |
893
|
|
|
* @param string $login the login to add |
894
|
|
|
* @param string $pass the password to add |
895
|
|
|
* @return boolean TRUE if the account has been created, or FALSE if an error occurred. |
896
|
|
|
*/ |
897
|
|
|
function add_slave_account($login, $pass) { |
898
|
|
|
global $db, $err; |
899
|
|
|
$db->query("SELECT * FROM mxaccount WHERE login= ? ;", array($login)); |
900
|
|
|
if ($db->next_record()) { |
901
|
|
|
$err->raise("mail", _("The slave MX account was not found")); |
902
|
|
|
return false; |
903
|
|
|
} |
904
|
|
|
$db->query("INSERT INTO mxaccount (login,pass) VALUES (?, ?);", array($login, $pass)); |
905
|
|
|
return true; |
906
|
|
|
} |
907
|
|
|
|
908
|
|
|
/* ----------------------------------------------------------------- */ |
909
|
|
|
|
910
|
|
|
/** |
911
|
|
|
* Remove a slave account |
912
|
|
|
* @param string $login the login to delete |
913
|
|
|
*/ |
914
|
|
|
function del_slave_account($login) { |
915
|
|
|
global $db; |
916
|
|
|
$db->query("DELETE FROM mxaccount WHERE login= ? ;", array($login)); |
917
|
|
|
return true; |
918
|
|
|
} |
919
|
|
|
|
920
|
|
|
/* ----------------------------------------------------------------- */ |
921
|
|
|
|
922
|
|
|
/** hook function called by AlternC when a domain is created for |
923
|
|
|
* the current user account using the SLAVE DOMAIN feature |
924
|
|
|
* This function create a CATCHALL to the master domain |
925
|
|
|
* @param string $domain_id Domain that has just been created |
926
|
|
|
* @param string $target_domain Master domain |
927
|
|
|
* @access private |
928
|
|
|
*/ |
929
|
|
|
function hook_dom_add_slave_domain($domain_id, $target_domain) { |
930
|
|
|
global $err; |
931
|
|
|
$err->log("mail", "hook_dom_add_slave_domain", $domain_id); |
932
|
|
|
$this->catchall_set($domain_id, '@' . $target_domain); |
933
|
|
|
return true; |
934
|
|
|
} |
935
|
|
|
|
936
|
|
|
/* ----------------------------------------------------------------- */ |
937
|
|
|
|
938
|
|
|
/** hook function called by AlternC when a domain is created for |
939
|
|
|
* the current user account |
940
|
|
|
* This function create a postmaster mail which is an alias to LOGIN @ FQDN |
941
|
|
|
* wich is a dynamic alias to the alternc's account mail |
942
|
|
|
* @param string $domain_id Domain that has just been created |
943
|
|
|
* @access private |
944
|
|
|
*/ |
945
|
|
|
function hook_dom_add_mx_domain($domain_id) { |
946
|
|
|
global $err, $mem, $db; |
947
|
|
|
$err->log("mail", "hook_dom_add_mx_domain", $domain_id); |
948
|
|
|
|
949
|
|
|
$db->query("SELECT value FROM variable where name='mailname_bounce';"); |
950
|
|
|
if (!$db->next_record()) { |
951
|
|
|
$err->raise("mail", _("Problem: can't create default bounce mail")); |
952
|
|
|
return false; |
953
|
|
|
} |
954
|
|
|
$mailname = $db->f("value"); |
955
|
|
|
// set spf & dmarc for this domain |
956
|
|
|
$db->query("SELECT domaine FROM domaines WHERE id= ?;", array($domain_id)); |
957
|
|
|
if ($db->next_record()) { |
958
|
|
|
if ($spf = variable_get("default_spf_value")) { |
959
|
|
|
$this->set_dns_spf($db->Record["domaine"], $spf); |
960
|
|
|
} |
961
|
|
|
if ($dmarc = variable_get("default_dmarc_value")) { |
962
|
|
|
$this->set_dns_dmarc($db->Record["domaine"], $dmarc); |
963
|
|
|
} |
964
|
|
|
} |
965
|
|
|
return $this->create_alias($domain_id, 'postmaster', $mem->user['login'] . '@' . $mailname); |
966
|
|
|
} |
967
|
|
|
|
968
|
|
|
/* ----------------------------------------------------------------- */ |
969
|
|
|
|
970
|
|
|
/** hook function called by variables when a variable is changed |
971
|
|
|
* @access private |
972
|
|
|
*/ |
973
|
|
|
function hook_variable_set($name, $old, $new) { |
974
|
|
|
global $err, $db; |
975
|
|
|
$err->log("mail", "hook_variable_set($name,$old,$new)"); |
976
|
|
|
|
977
|
|
|
if ($name == "default_spf_value") { |
978
|
|
|
$new = trim($new); |
979
|
|
|
$old = trim($old); |
980
|
|
|
$db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); |
981
|
|
|
$res=array(); |
982
|
|
|
while ($db->next_record()) $res[]=$db->Record; |
983
|
|
|
foreach ($res as $record) { |
984
|
|
|
$this->set_dns_spf($record["domaine"], $new, $old, $record["compte"], $record["login"]); |
985
|
|
|
} |
986
|
|
|
} |
987
|
|
|
|
988
|
|
|
if ($name == "default_dmarc_value") { |
989
|
|
|
$new = trim($new); |
990
|
|
|
$old = trim($old); |
991
|
|
|
$db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); |
992
|
|
|
$res=array(); |
993
|
|
|
while ($db->next_record()) $res[]=$db->Record; |
994
|
|
|
foreach ($res as $record) { |
995
|
|
|
$this->set_dns_dmarc($record["domaine"], $new, $old, $record["compte"], $record["login"]); |
996
|
|
|
} |
997
|
|
|
} |
998
|
|
|
} |
999
|
|
|
|
1000
|
|
|
/* ----------------------------------------------------------------- */ |
1001
|
|
|
|
1002
|
|
|
/** Set or UPDATE the DNS record for the domain $dom(str) to be $spf |
1003
|
|
|
* account's login is current and if not it's $login. |
1004
|
|
|
* don't change spf if current value is not $old |
1005
|
|
|
* @access private |
1006
|
|
|
*/ |
1007
|
|
|
function set_dns_spf($domain, $spf, $previous = -1, $uid = -1, $login = -1) { |
1008
|
|
|
global $db, $cuid, $mem; |
1009
|
|
|
// defaults |
1010
|
|
|
if ($uid === -1) { |
1011
|
|
|
$uid = intval($cuid); |
1012
|
|
|
} else { |
1013
|
|
|
$uid = intval($uid); |
1014
|
|
|
} |
1015
|
|
|
if ($login === -1) { |
1016
|
|
|
$login = $mem->user["login"]; |
1017
|
|
|
} |
1018
|
|
|
// Search for the record in sub_domaines table |
1019
|
|
|
$db->query("SELECT * FROM sub_domaines WHERE compte= ? AND domaine= ? AND sub='' AND type='txt' AND valeur LIKE 'v=spf1 %' AND web_action!='DELETE';", array($uid, $domain)); |
1020
|
|
|
if ($db->next_record()) { |
1021
|
|
|
if ($previous !== -1 && $db->Record["valeur"] == "v=spf1 " . $spf) { |
1022
|
|
|
return; // skip, no change asked. |
1023
|
|
|
} |
1024
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id= ? ;",array($db->Record["id"])); |
1025
|
|
|
} |
1026
|
|
|
$db->query("INSERT INTO sub_domaines SET compte= ?, domaine= ?, sub='', type='txt', valeur= ? , web_action='UPDATE';", array($uid, $domain, "v=spf1 " . $spf)); |
1027
|
|
|
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain)); |
1028
|
|
|
} |
1029
|
|
|
|
1030
|
|
|
/* ----------------------------------------------------------------- */ |
1031
|
|
|
|
1032
|
|
|
/** Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc |
1033
|
|
|
* account's login is current and if not it's $login. |
1034
|
|
|
* don't change dmarc if current value is not $old |
1035
|
|
|
* @access private |
1036
|
|
|
*/ |
1037
|
|
|
function set_dns_dmarc($domain, $dmarc, $previous = -1, $uid = -1, $login = -1) { |
1038
|
|
|
global $db, $cuid, $mem, $L_FQDN; |
1039
|
|
|
// defaults |
1040
|
|
|
if ($uid === -1) { |
1041
|
|
|
$uid = intval($cuid); |
1042
|
|
|
} else { |
1043
|
|
|
$uid = intval($uid); |
1044
|
|
|
} |
1045
|
|
|
if ($login === -1) { |
1046
|
|
|
$login = $mem->user["login"]; |
1047
|
|
|
} |
1048
|
|
|
$dmarc = str_replace("%%ADMINMAIL%%", "admin@" . $L_FQDN, $dmarc); |
1049
|
|
|
$dmarc = str_replace("%%USERMAIL%%", $login . "@" . $L_FQDN, $dmarc); |
1050
|
|
|
|
1051
|
|
|
// Search for the record in sub_domaines table |
1052
|
|
|
$db->query("SELECT * FROM sub_domaines WHERE compte= ? AND domaine= ? AND sub='_dmarc' AND type='txt' AND valeur LIKE 'v=dmarc1;%' AND web_action!='DELETE';", array($uid, $domain)); |
1053
|
|
|
if ($db->next_record()) { |
1054
|
|
|
if ($previous !== -1 && $db->Record["valeur"] == "v=dmarc1;" . $dmarc) { |
1055
|
|
|
return; // skip, no change asked. |
1056
|
|
|
} |
1057
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id= ?;", array($db->Record["id"])); |
1058
|
|
|
} |
1059
|
|
|
$db->query("INSERT INTO sub_domaines SET compte= ?, domaine= ?, sub='_dmarc', type='txt', valeur= ?, web_action='UPDATE';", array($uid, $domain, "v=dmarc1;" . $dmarc)); |
1060
|
|
|
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine= ?;", array($domain)); |
1061
|
|
|
} |
1062
|
|
|
|
1063
|
|
|
/* ----------------------------------------------------------------- */ |
1064
|
|
|
|
1065
|
|
|
/** hook function called by AlternC-upnp to know which open |
1066
|
|
|
* tcp or udp ports this class requires or suggests |
1067
|
|
|
* @return array a key => value list of port protocol name mandatory values |
1068
|
|
|
* @access private |
1069
|
|
|
*/ |
1070
|
|
|
function hook_upnp_list() { |
1071
|
|
|
return array( |
1072
|
|
|
"imap" => array("port" => 143, "protocol" => "tcp", "mandatory" => 1), |
1073
|
|
|
"imaps" => array("port" => 993, "protocol" => "tcp", "mandatory" => 1), |
1074
|
|
|
"pop" => array("port" => 110, "protocol" => "tcp", "mandatory" => 1), |
1075
|
|
|
"pops" => array("port" => 995, "protocol" => "tcp", "mandatory" => 1), |
1076
|
|
|
"smtp" => array("port" => 25, "protocol" => "tcp", "mandatory" => 1), |
1077
|
|
|
"sieve" => array("port" => 2000, "protocol" => "tcp", "mandatory" => 1), |
1078
|
|
|
"submission" => array("port" => 587, "protocol" => "tcp", "mandatory" => 0), |
1079
|
|
|
); |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
} |
1083
|
|
|
|
1084
|
|
|
/* Class m_mail */ |
1085
|
|
|
|
In PHP, under loose comparison (like
==
, or!=
, orswitch
conditions), values of different types might be equal.For
string
values, the empty string''
is a special case, in particular the following results might be unexpected: