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 = $domain_id and r.address_id = a.id and a.address='';"); |
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=$cuid AND a.type='';"); |
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 = $uid |
243
|
|
|
and d.gesmx = 1 |
244
|
|
|
GROUP BY |
245
|
|
|
d.id |
246
|
|
|
ORDER BY |
247
|
|
|
d.domaine |
248
|
|
|
; |
249
|
|
|
"); |
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=" . $dom_id . " AND a.address='" . addslashes($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
|
|
|
$search = trim($search); |
300
|
|
|
|
301
|
|
|
$where = "a.domain_id=$dom_id"; |
302
|
|
|
if ($search) { |
303
|
|
|
$where.=" AND (a.address LIKE '%" . addslashes($search) . "%' OR r.recipients LIKE '%" . addslashes($search) . "%')"; |
304
|
|
|
} |
305
|
|
|
if (!$show_systemmails) { |
306
|
|
|
$where.=" AND type='' "; |
307
|
|
|
} |
308
|
|
|
$db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE $where;"); |
309
|
|
|
$db->next_record(); |
310
|
|
|
$this->total = $db->f("total"); |
311
|
|
|
if ($count != -1) { |
312
|
|
|
$limit = "LIMIT $offset,$count"; |
313
|
|
|
} else { |
314
|
|
|
$limit = ""; |
315
|
|
|
} |
316
|
|
|
$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 |
317
|
|
|
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 |
318
|
|
|
WHERE $where AND d.id=a.domain_id $limit ;"); |
319
|
|
|
if (!$db->next_record()) { |
320
|
|
|
$err->raise("mail", _("No email found for this query")); |
321
|
|
|
return array(); |
322
|
|
|
} |
323
|
|
|
$res = array(); |
324
|
|
|
do { |
325
|
|
|
$details = $db->Record; |
326
|
|
|
// if necessary, fill the typedata with data from hooks ... |
327
|
|
|
if ($details["type"]) { |
328
|
|
|
$result = $hooks->invoke("hook_mail_get_details", array($details)); // Will fill typedata if necessary |
329
|
|
|
$details["typedata"] = implode("<br />", $result); |
330
|
|
|
} |
331
|
|
|
$res[] = $details; |
332
|
|
|
} while ($db->next_record()); |
333
|
|
|
return $res; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
function hook_mail_get_details($detail) { |
337
|
|
|
if ($detail['type'] == 'catchall') { |
338
|
|
|
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'])); |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/* ----------------------------------------------------------------- */ |
343
|
|
|
|
344
|
|
|
/** Function used to insert a new mail into the db |
345
|
|
|
* should be used by the web interface, not by third-party programs. |
346
|
|
|
* |
347
|
|
|
* This function calls the hook "hooks_mail_cancreate" |
348
|
|
|
* which must return FALSE if the user can't create this email, and raise and error accordingly |
349
|
|
|
* |
350
|
|
|
* @param $dom_id integer A domain_id (owned by the user) |
351
|
|
|
* (will be the part at the right of the @ in the email) |
352
|
|
|
* @param $mail string the left part of the email to create (something@dom_id) |
353
|
|
|
* @return an hashtable containing the database id of the newly created mail, |
354
|
|
|
* or false if an error occured ($err is filled accordingly) |
355
|
|
|
*/ |
356
|
|
|
function create($dom_id, $mail, $type = "", $dontcheck = false) { |
357
|
|
|
global $err, $db, $quota, $dom, $hooks; |
358
|
|
|
$err->log("mail", "create", $mail); |
359
|
|
|
|
360
|
|
|
// Validate the domain id |
361
|
|
|
if (!($domain = $dom->get_domain_byid($dom_id))) { |
362
|
|
|
return false; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
// Validate the email syntax: |
366
|
|
|
$m = $mail . "@" . $domain; |
367
|
|
|
if (!filter_var($m, FILTER_VALIDATE_EMAIL) && !$dontcheck) { |
368
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
369
|
|
|
return false; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
// Call other classes to check we can create it: |
373
|
|
|
$cancreate = $hooks->invoke("hook_mail_cancreate", array($dom_id, $mail)); |
374
|
|
|
if (in_array(false, $cancreate, true)) { |
375
|
|
|
return false; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
// Check the quota: |
379
|
|
|
if (!$quota->cancreate("mail")) { |
380
|
|
|
$err->raise("mail", _("You cannot create email addresses: your quota is over")); |
381
|
|
|
return false; |
382
|
|
|
} |
383
|
|
|
// Already exists? |
384
|
|
|
$db->query("SELECT * FROM address WHERE domain_id=" . $dom_id . " AND address='" . addslashes($mail) . "';"); |
385
|
|
|
if ($db->next_record()) { |
386
|
|
|
$err->raise("mail", _("This email address already exists")); |
387
|
|
|
return false; |
388
|
|
|
} |
389
|
|
|
// Create it now |
390
|
|
|
$db->query("INSERT INTO address (domain_id, address,type) VALUES ($dom_id, '" . addslashes($mail) . "','$type');"); |
391
|
|
|
if (!($id = $db->lastid())) { |
392
|
|
|
$err->raise("mail", _("An unexpected error occured when creating the email")); |
393
|
|
|
return false; |
394
|
|
|
} |
395
|
|
|
return $id; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/* ----------------------------------------------------------------- */ |
399
|
|
|
|
400
|
|
|
/** function used to get every information we can on a mail |
401
|
|
|
* @param $mail_id integer |
402
|
|
|
* @return array a hashtable with all the informations for that email |
403
|
|
|
*/ |
404
|
|
|
function get_details($mail_id) { |
405
|
|
|
global $db, $err, $hooks; |
406
|
|
|
$err->log("mail", "get_details"); |
407
|
|
|
|
408
|
|
|
$mail_id = intval($mail_id); |
409
|
|
|
// Validate that this email is owned by me... |
410
|
|
|
if (!($mail = $this->is_it_my_mail($mail_id))) { |
411
|
|
|
return false; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
// We fetch all the informations for that email: these will fill the hastable : |
415
|
|
|
$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=" . $mail_id . " AND d.id=a.domain_id;"); |
416
|
|
|
if (!$db->next_record()) { |
417
|
|
|
return false; |
418
|
|
|
} |
419
|
|
|
$details = $db->Record; |
420
|
|
|
// if necessary, fill the typedata with data from hooks ... |
421
|
|
|
if ($details["type"]) { |
422
|
|
|
$result = $hooks->invoke("hook_mail_get_details", array($mail_id)); // Will fill typedata if necessary |
423
|
|
|
$details["typedata"] = implode("<br />", $result); |
424
|
|
|
} |
425
|
|
|
return $details; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
private $isitmy_cache = array(); |
429
|
|
|
|
430
|
|
|
/* ----------------------------------------------------------------- */ |
431
|
|
|
|
432
|
|
|
/** Check if an email is mine ... |
433
|
|
|
* |
434
|
|
|
* @param $mail_id integer the number of the email to check |
435
|
|
|
* @return string the complete email address if that's mine, false if not |
436
|
|
|
* ($err is filled accordingly) |
437
|
|
|
*/ |
438
|
|
|
function is_it_my_mail($mail_id) { |
439
|
|
|
global $err, $db, $cuid; |
440
|
|
|
$mail_id = intval($mail_id); |
441
|
|
|
// cache it (may be called more than one time in the same page). |
442
|
|
|
if (isset($this->isitmy_cache[$mail_id])) { |
443
|
|
|
return $this->isitmy_cache[$mail_id]; |
444
|
|
|
} |
445
|
|
|
$db->query("SELECT concat(a.address,'@',d.domaine) AS email FROM address a, domaines d WHERE d.id=a.domain_id AND a.id=$mail_id AND d.compte=$cuid;"); |
446
|
|
|
if ($db->next_record()) { |
447
|
|
|
return $this->isitmy_cache[$mail_id] = $db->f("email"); |
448
|
|
|
} else { |
449
|
|
|
$err->raise("mail", _("This email is not yours, you can't change anything on it")); |
450
|
|
|
return $this->isitmy_cache[$mail_id] = false; |
451
|
|
|
} |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
/* ----------------------------------------------------------------- */ |
455
|
|
|
|
456
|
|
|
/** Hook called when the DOMAIN class will delete a domain. |
457
|
|
|
* |
458
|
|
|
* @param $dom integer the number of the email to delete |
459
|
|
|
* @return boolean if the email has been properly deleted |
460
|
|
|
* or false if an error occured ($err is filled accordingly) |
461
|
|
|
*/ |
462
|
|
|
function hook_dom_del_mx_domain($dom_id) { |
463
|
|
|
global $db; |
464
|
|
|
$list = $this->enum_domain_mails($dom_id, "", 0, -1); |
465
|
|
|
if (is_array($list)) { |
466
|
|
|
foreach ($list as $one) { |
467
|
|
|
$this->delete($one["id"]); |
468
|
|
|
} |
469
|
|
|
} |
470
|
|
|
$db->query("SELECT domaine FROM domaines WHERE id=$domain_id;"); |
471
|
|
|
if ($db->next_record()) { |
472
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine='" . addslashes($db->Record["domaine"]) . "' AND type='txt' AND (sub='' AND valeur LIKE 'v=spf1 %') OR (sub='_dmarc' AND valeur LIKE 'v=dmarc1;%');"); |
473
|
|
|
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE id=$domain_id;"); |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
return true; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
// return the alternc account's ID of the mail_id |
480
|
|
|
function get_account_by_mail_id($mail_id) { |
481
|
|
|
global $db; |
482
|
|
|
$db->query("select compte as uid from domaines d, address a where a.domain_id = d.id and a.id = $mail_id"); |
483
|
|
|
if (!$db->next_record()) { |
484
|
|
|
return false; |
485
|
|
|
} |
486
|
|
|
return $db->f('uid'); |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/* ----------------------------------------------------------------- */ |
490
|
|
|
|
491
|
|
|
/** Function used to delete a mail from the db |
492
|
|
|
* should be used by the web interface, not by third-party programs. |
493
|
|
|
* |
494
|
|
|
* @param $mail_id integer the number of the email to delete |
495
|
|
|
* @return boolean if the email has been properly deleted |
496
|
|
|
* or false if an error occured ($err is filled accordingly) |
497
|
|
|
*/ |
498
|
|
|
function delete($mail_id) { |
499
|
|
|
global $err, $db, $hooks; |
500
|
|
|
$err->log("mail", "delete"); |
501
|
|
|
|
502
|
|
|
$mail_id = intval($mail_id); |
503
|
|
|
|
504
|
|
|
if (!$mail_id) { |
505
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
506
|
|
|
return false; |
507
|
|
|
} |
508
|
|
|
// Validate that this email is owned by me... |
509
|
|
|
if (!($mail = $this->is_it_my_mail($mail_id))) { |
510
|
|
|
return false; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
$mailinfos = $this->get_details($mail_id); |
514
|
|
|
$hooks->invoke('hook_mail_delete', array($mail_id, $mailinfos['address'] . '@' . $mailinfos['domain'])); |
515
|
|
|
|
516
|
|
|
// Search for that address: |
517
|
|
|
$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='$mail_id';"); |
518
|
|
|
if (!$db->next_record()) { |
519
|
|
|
$err->raise("mail", _("The email %s does not exist, it can't be deleted"), $mail); |
520
|
|
|
return false; |
521
|
|
|
} |
522
|
|
|
if ($db->f("mail_action") != "OK" || ($db->f("islocal") && $db->f("mailbox_action") != "OK")) { // will be deleted soon ... |
523
|
|
|
$err->raise("mail", _("The email %s is already marked for deletion, it can't be deleted"), $mail); |
524
|
|
|
return false; |
525
|
|
|
} |
526
|
|
|
$mail_id = $db->f("id"); |
527
|
|
|
|
528
|
|
|
if ($db->f("islocal")) { |
529
|
|
|
// If it's a pop/imap mailbox, mark it for deletion |
530
|
|
|
$db->query("UPDATE address SET mail_action='DELETE', enabled=0 WHERE id='$mail_id';"); |
531
|
|
|
$db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id='$mail_id';"); |
532
|
|
|
$err->raise("mail", _("The email %s has been marked for deletion"), $mail); |
533
|
|
|
} else { |
534
|
|
|
// If it's only aliases, delete it NOW. |
535
|
|
|
$db->query("DELETE FROM address WHERE id='$mail_id';"); |
536
|
|
|
$db->query("DELETE FROM mailbox WHERE address_id='$mail_id';"); |
537
|
|
|
$db->query("DELETE FROM recipient WHERE address_id='$mail_id';"); |
538
|
|
|
$err->raise("mail", _("The email %s has been successfully deleted"), $mail); |
539
|
|
|
} |
540
|
|
|
return true; |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
/* ----------------------------------------------------------------- */ |
544
|
|
|
|
545
|
|
|
/** Function used to undelete a pending deletion mail from the db |
546
|
|
|
* should be used by the web interface, not by third-party programs. |
547
|
|
|
* |
548
|
|
|
* @param $mail_id integer the email id |
549
|
|
|
* @return boolean if the email has been properly undeleted |
550
|
|
|
* or false if an error occured ($err is filled accordingly) |
551
|
|
|
*/ |
552
|
|
|
function undelete($mail_id) { |
553
|
|
|
global $err, $db; |
554
|
|
|
$err->log("mail", "undelete"); |
555
|
|
|
|
556
|
|
|
$mail_id = intval($mail_id); |
557
|
|
|
|
558
|
|
|
if (!$mail_id) { |
559
|
|
|
$err->raise("mail", _("The email you entered is syntaxically incorrect")); |
560
|
|
|
return false; |
561
|
|
|
} |
562
|
|
|
// Validate that this email is owned by me... |
563
|
|
|
if (!($mail = $this->is_it_my_mail($mail_id))) { |
564
|
|
|
return false; |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
// Search for that address: |
568
|
|
|
$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='$mail_id';"); |
569
|
|
|
if (!$db->next_record()) { |
570
|
|
|
$err->raise("mail", _("The email %s does not exist, it can't be undeleted"), $mail); |
571
|
|
|
return false; |
572
|
|
|
} |
573
|
|
|
if ($db->f("type") != "") { // Technically special : mailman, sympa ... |
574
|
|
|
$err->raise("mail", _("The email %s is special, it can't be undeleted"), $mail); |
575
|
|
|
return false; |
576
|
|
|
} |
577
|
|
|
if ($db->f("mailbox_action") != "DELETE" || $db->f("mail_action") != "DELETE") { // will be deleted soon ... |
578
|
|
|
$err->raise("mail", _("Sorry, deletion of email %s is already in progress, or not marked for deletion, it can't be undeleted"), $mail); |
579
|
|
|
return false; |
580
|
|
|
} |
581
|
|
|
$mail_id = $db->f("id"); |
582
|
|
|
|
583
|
|
|
if ($db->f("islocal")) { |
584
|
|
|
// If it's a pop/imap mailbox, mark it for deletion |
585
|
|
|
$db->query("UPDATE address SET mail_action='OK', `enabled`=1 WHERE id='$mail_id';"); |
586
|
|
|
$db->query("UPDATE mailbox SET mail_action='OK' WHERE address_id='$mail_id';"); |
587
|
|
|
$err->raise("mail", _("The email %s has been undeleted"), $mail); |
588
|
|
|
return true; |
589
|
|
|
} else { |
590
|
|
|
$err->raise("mail", _("-- Program Error -- The email %s can't be undeleted"), $mail); |
591
|
|
|
return false; |
592
|
|
|
} |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
/* ----------------------------------------------------------------- */ |
596
|
|
|
|
597
|
|
|
/** set the password of an email address. |
598
|
|
|
* @param $mail_id integer email ID |
599
|
|
|
* @param $pass string the new password. |
600
|
|
|
* @return boolean true if the password has been set, false else, raise an error. |
601
|
|
|
*/ |
602
|
|
|
function set_passwd($mail_id, $pass) { |
603
|
|
|
global $db, $err, $admin; |
604
|
|
|
$err->log("mail", "setpasswd"); |
605
|
|
|
|
606
|
|
|
if (!($email = $this->is_it_my_mail($mail_id))) { |
607
|
|
|
return false; |
608
|
|
|
} |
609
|
|
|
if (!$admin->checkPolicy("pop", $email, $pass)) { |
610
|
|
|
return false; |
611
|
|
|
} |
612
|
|
|
if (!$db->query("UPDATE address SET password='" . _md5cr($pass) . "' where id=$mail_id;")) { |
613
|
|
|
return false; |
614
|
|
|
} |
615
|
|
|
return true; |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
/* ----------------------------------------------------------------- */ |
619
|
|
|
|
620
|
|
|
/** Enables an email address. |
621
|
|
|
* @param $mail_id integer Email ID |
622
|
|
|
* @return boolean true if the email has been enabled. |
623
|
|
|
*/ |
624
|
|
|
function enable($mail_id) { |
625
|
|
|
global $db, $err; |
626
|
|
|
$err->log("mail", "enable"); |
627
|
|
|
if (!($email = $this->is_it_my_mail($mail_id))) { |
628
|
|
|
return false; |
629
|
|
|
} |
630
|
|
|
if (!$db->query("UPDATE address SET `enabled`=1 where id=$mail_id;")) { |
631
|
|
|
return false; |
632
|
|
|
} |
633
|
|
|
return true; |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/* ----------------------------------------------------------------- */ |
637
|
|
|
|
638
|
|
|
/** Disables an email address. |
639
|
|
|
* @param $mail_id integer Email ID |
640
|
|
|
* @return boolean true if the email has been enabled. |
641
|
|
|
*/ |
642
|
|
|
function disable($mail_id) { |
643
|
|
|
global $db, $err; |
644
|
|
|
$err->log("mail", "disable"); |
645
|
|
|
if (!($email = $this->is_it_my_mail($mail_id))) { |
646
|
|
|
return false; |
647
|
|
|
} |
648
|
|
|
if (!$db->query("UPDATE address SET `enabled`=0 where id=$mail_id;")) { |
649
|
|
|
return false; |
650
|
|
|
} |
651
|
|
|
return true; |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
/* ----------------------------------------------------------------- */ |
655
|
|
|
|
656
|
|
|
/** Function used to update an email settings |
657
|
|
|
* should be used by the web interface, not by third-party programs. |
658
|
|
|
* |
659
|
|
|
* @param $mail_id integer the number of the email to delete |
660
|
|
|
* @param integer $islocal boolean is it a POP/IMAP mailbox ? |
661
|
|
|
* @param integer $quotamb integer if islocal=1, quota in MB |
662
|
|
|
* @param string $recipients string recipients, one mail per line. |
663
|
|
|
* @return boolean if the email has been properly edited |
664
|
|
|
* or false if an error occured ($err is filled accordingly) |
665
|
|
|
*/ |
666
|
|
|
function set_details($mail_id, $islocal, $quotamb, $recipients, $delivery = "dovecot", $dontcheck = false) { |
667
|
|
|
global $err, $db; |
668
|
|
|
$delivery = mysql_real_escape_string($delivery); |
669
|
|
|
$err->log("mail", "set_details"); |
670
|
|
|
if (!($me = $this->get_details($mail_id))) { |
671
|
|
|
return false; |
672
|
|
|
} |
673
|
|
|
if ($me["islocal"] && !$islocal) { |
674
|
|
|
// delete pop |
675
|
|
|
$db->query("UPDATE mailbox SET mail_action='DELETE' WHERE address_id=" . $mail_id . ";"); |
676
|
|
|
} |
677
|
|
|
if (!$me["islocal"] && $islocal) { |
678
|
|
|
// create pop |
679
|
|
|
$path = ""; |
680
|
|
|
if ($delivery == "dovecot") { |
681
|
|
|
$path = ALTERNC_MAIL . "/" . substr($me["address"] . "_", 0, 1) . "/" . $me["address"] . "_" . $me["domain"]; |
682
|
|
|
} |
683
|
|
|
foreach ($this->forbiddenchars as $str) { |
684
|
|
|
if (strpos($me["address"], $str) !== false) { |
685
|
|
|
$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")); |
686
|
|
|
return false; |
687
|
|
|
} |
688
|
|
|
} |
689
|
|
|
foreach ($this->specialchars as $str) { |
690
|
|
|
if (strpos($me["address"], $str) !== false) { |
691
|
|
|
$path = ALTERNC_MAIL . "/_/" . $me["id"] . "_" . $me["domain"]; |
692
|
|
|
break; |
693
|
|
|
} |
694
|
|
|
} |
695
|
|
|
$db->query("INSERT INTO mailbox SET address_id=$mail_id, delivery='$delivery', path='" . addslashes($path) . "';"); |
696
|
|
|
} |
697
|
|
|
if ($me["islocal"] && $islocal && $me["mailbox_action"] == "DELETE") { |
698
|
|
|
$db->query("UPDATE mailbox SET mail_action='OK' WHERE mail_action='DELETE' AND address_id=" . $mail_id . ";"); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
if ($islocal) { |
702
|
|
|
if ($quotamb != 0 && $quotamb < (intval($me["used"] / 1024 / 1024) + 1)) { |
703
|
|
|
$quotamb = intval($me["used"] / 1024 / 1024) + 1; |
704
|
|
|
$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")); |
705
|
|
|
} |
706
|
|
|
$db->query("UPDATE mailbox SET quota=" . intval($quotamb) . " WHERE address_id=" . $mail_id . ";"); |
707
|
|
|
} |
708
|
|
|
|
709
|
|
|
$recipients = preg_replace('/[\r\t\s]/', "\n", $recipients); // Handle space AND new line |
710
|
|
|
$r = explode("\n", $recipients); |
711
|
|
|
$red = ""; |
712
|
|
|
foreach ($r as $m) { |
713
|
|
|
$m = trim($m); |
714
|
|
|
if ($m && ( filter_var($m, FILTER_VALIDATE_EMAIL) || $dontcheck) // Recipient Email is valid |
715
|
|
|
&& $m != ($me["address"] . "@" . $me["domain"])) { // And not myself (no loop allowed easily ;) ) |
716
|
|
|
$red.=$m . "\n"; |
717
|
|
|
} |
718
|
|
|
} |
719
|
|
|
$db->query("DELETE FROM recipient WHERE address_id=" . $mail_id . ";"); |
720
|
|
|
if (isset($red) && $red) { |
721
|
|
|
$db->query("INSERT INTO recipient SET address_id=" . $mail_id . ", recipients='" . addslashes($red) . "';"); |
722
|
|
|
} |
723
|
|
|
if (!$islocal && !$red) { |
|
|
|
|
724
|
|
|
$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.")); |
725
|
|
|
} |
726
|
|
|
return true; |
727
|
|
|
} |
728
|
|
|
|
729
|
|
|
/* ----------------------------------------------------------------- */ |
730
|
|
|
|
731
|
|
|
/** A wrapper used by mailman class to create it's needed addresses |
732
|
|
|
* @ param : $dom_id , the domain id associated to a given address |
733
|
|
|
* @ param : $m , the left part of the mail address being created |
734
|
|
|
* @ param : $delivery , the delivery used to deliver the mail |
735
|
|
|
*/ |
736
|
|
|
function add_wrapper($dom_id, $m, $delivery) { |
737
|
|
|
global $err, $mail; |
738
|
|
|
$err->log("mail", "add_wrapper", "creating $delivery $m address"); |
739
|
|
|
|
740
|
|
|
$mail_id = $mail->create($dom_id, $m, $delivery); |
741
|
|
|
$this->set_details($mail_id, 1, 0, '', $delivery); |
742
|
|
|
// FIXME return error code |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
/* ----------------------------------------------------------------- */ |
746
|
|
|
|
747
|
|
|
/** A function used to create an alias for a specific address |
748
|
|
|
* @ param : $dom_id , the domain sql identifier |
749
|
|
|
* @ param : $m , the alias we want to create |
750
|
|
|
* @ param : $alias , the already existing aliased address |
751
|
|
|
* @ param : $type, the type of the alias created |
752
|
|
|
* @param string $m |
753
|
|
|
* @param string $alias |
754
|
|
|
* @param string $dom_id |
755
|
|
|
*/ |
756
|
|
|
function create_alias($dom_id, $m, $alias, $type = "", $dontcheck = false) { |
757
|
|
|
global $err, $mail; |
758
|
|
|
$err->log("mail", "create_alias", "creating $m alias for $alias type $type"); |
759
|
|
|
|
760
|
|
|
$mail_id = $mail->create($dom_id, $m, $type, $dontcheck); |
761
|
|
|
if (!$mail_id) { |
762
|
|
|
return false; |
763
|
|
|
} |
764
|
|
|
$this->set_details($mail_id, 0, 0, $alias, "dovecot", $dontcheck); |
765
|
|
|
return true; |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
/* ----------------------------------------------------------------- */ |
769
|
|
|
|
770
|
|
|
/** A wrapper used by mailman class to create it's needed addresses |
771
|
|
|
* @ param : $mail_id , the mysql id of the mail address we want to delete |
772
|
|
|
* of the email for the current acccount. |
773
|
|
|
*/ |
774
|
|
|
function del_wrapper($mail_id) { |
775
|
|
|
global $err; |
776
|
|
|
$err->log("mail", "del_wrapper"); |
777
|
|
|
$this->delete($mail_id); |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
/* ----------------------------------------------------------------- */ |
781
|
|
|
|
782
|
|
|
/** Export the mail information of an account |
783
|
|
|
* @return: str, string containing the complete configuration |
784
|
|
|
* of the email for the current acccount. |
785
|
|
|
*/ |
786
|
|
|
function alternc_export_conf() { |
787
|
|
|
global $err; |
788
|
|
|
$err->log("mail", "export"); |
789
|
|
|
$domain = $this->enum_domains(); |
790
|
|
|
$str = "<mail>\n"; |
791
|
|
|
foreach ($domain as $d) { |
792
|
|
|
$str.=" <domain>\n <name>" . xml_entities($d["domain"]) . "</name>\n"; |
793
|
|
|
$s = $this->enum_domain_mails($d["id"]); |
794
|
|
|
if (count($s)) { |
795
|
|
|
while (list($key, $val) = each($s)) { |
796
|
|
|
$str.=" <address>\n"; |
797
|
|
|
$str.=" <name>" . xml_entities($val["address"]) . "</name>\n"; |
798
|
|
|
$str.=" <enabled>" . xml_entities($val["enabled"]) . "</enabled>\n"; |
799
|
|
|
if (is_array($val["islocal"])) { |
800
|
|
|
$str.=" <islocal>1</islocal>\n"; |
801
|
|
|
$str.=" <quota>" . $val["quota"] . "</quota>\n"; |
802
|
|
|
$str.=" <path>" . $val["path"] . "</path>\n"; |
803
|
|
|
} else { |
804
|
|
|
$str.=" <islocal>0</islocal>\n"; |
805
|
|
|
} |
806
|
|
|
if (!empty($val["recipients"])) { |
807
|
|
|
$r = explode("\n", $val["recipients"]); |
808
|
|
|
foreach ($r as $recip) { |
809
|
|
|
$str.=" <recipients>" . $recip . "<recipients>\n"; |
810
|
|
|
} |
811
|
|
|
} |
812
|
|
|
$str.=" </address>\n"; |
813
|
|
|
} |
814
|
|
|
} |
815
|
|
|
$str.=" </domain>\n"; |
816
|
|
|
} |
817
|
|
|
$str.="</mail>\n"; |
818
|
|
|
return $str; |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
/* ----------------------------------------------------------------- */ |
822
|
|
|
|
823
|
|
|
/** |
824
|
|
|
* Return the list of allowed slave accounts (secondary-mx) |
825
|
|
|
* @return array |
826
|
|
|
*/ |
827
|
|
|
function enum_slave_account() { |
828
|
|
|
global $db; |
829
|
|
|
$db->query("SELECT login,pass FROM mxaccount;"); |
830
|
|
|
$res = array(); |
831
|
|
|
while ($db->next_record()) { |
832
|
|
|
$res[] = $db->Record; |
833
|
|
|
} |
834
|
|
|
if (!count($res)) { |
835
|
|
|
return false; |
836
|
|
|
} |
837
|
|
|
return $res; |
838
|
|
|
} |
839
|
|
|
|
840
|
|
|
/* ----------------------------------------------------------------- */ |
841
|
|
|
|
842
|
|
|
/** |
843
|
|
|
* Check for a slave account (secondary mx) |
844
|
|
|
* @param string $login the login to check |
845
|
|
|
* @param string $pass the password to check |
846
|
|
|
* @return boolean TRUE if the password is correct, or FALSE if an error occurred. |
847
|
|
|
*/ |
848
|
|
|
function check_slave_account($login, $pass) { |
849
|
|
|
global $db; |
850
|
|
|
$login = mysql_real_escape_string($login); |
851
|
|
|
$pass = mysql_real_escape_string($pass); |
852
|
|
|
$db->query("SELECT * FROM mxaccount WHERE login='$login' AND pass='$pass';"); |
853
|
|
|
if ($db->next_record()) { |
854
|
|
|
return true; |
855
|
|
|
} |
856
|
|
|
return false; |
857
|
|
|
} |
858
|
|
|
|
859
|
|
|
/* ----------------------------------------------------------------- */ |
860
|
|
|
|
861
|
|
|
/** Out (echo) the complete hosted domain list : |
862
|
|
|
*/ |
863
|
|
|
function echo_domain_list($format = null) { |
864
|
|
|
global $db; |
865
|
|
|
$db->query("SELECT domaine FROM domaines WHERE gesmx=1 ORDER BY domaine"); |
866
|
|
|
$lst = array(); |
867
|
|
|
$tt = ""; |
868
|
|
|
while ($db->next_record()) { |
869
|
|
|
$lst[] = $db->f("domaine"); |
870
|
|
|
$tt.=$db->f("domaine"); |
871
|
|
|
} |
872
|
|
|
|
873
|
|
|
# Generate an integrity check |
874
|
|
|
$obj = array('integrity' => md5($tt), 'items' => $lst); |
875
|
|
|
|
876
|
|
|
switch ($format) { |
877
|
|
|
case "json": |
878
|
|
|
return json_encode($obj); |
879
|
|
|
default: |
880
|
|
|
foreach ($lst as $l) { |
881
|
|
|
echo $l . "\n"; |
882
|
|
|
} |
883
|
|
|
return true; |
884
|
|
|
} // switch |
885
|
|
|
} |
886
|
|
|
|
887
|
|
|
/* ----------------------------------------------------------------- */ |
888
|
|
|
|
889
|
|
|
/** |
890
|
|
|
* Add a slave account that will be allowed to access the mxdomain list |
891
|
|
|
* @param string $login the login to add |
892
|
|
|
* @param string $pass the password to add |
893
|
|
|
* @return boolean TRUE if the account has been created, or FALSE if an error occurred. |
894
|
|
|
*/ |
895
|
|
|
function add_slave_account($login, $pass) { |
896
|
|
|
global $db, $err; |
897
|
|
|
$login = mysql_real_escape_string($login); |
898
|
|
|
$pass = mysql_real_escape_string($pass); |
899
|
|
|
$db->query("SELECT * FROM mxaccount WHERE login='$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 ('$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
|
|
|
$login = mysql_real_escape_string($login); |
917
|
|
|
$db->query("DELETE FROM mxaccount WHERE login='$login'"); |
918
|
|
|
return true; |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
/* ----------------------------------------------------------------- */ |
922
|
|
|
|
923
|
|
|
/** hook function called by AlternC when a domain is created for |
924
|
|
|
* the current user account using the SLAVE DOMAIN feature |
925
|
|
|
* This function create a CATCHALL to the master domain |
926
|
|
|
* @param string $domain_id Domain that has just been created |
927
|
|
|
* @param string $target_domain Master domain |
928
|
|
|
* @access private |
929
|
|
|
*/ |
930
|
|
|
function hook_dom_add_slave_domain($domain_id, $target_domain) { |
931
|
|
|
global $err; |
932
|
|
|
$err->log("mail", "hook_dom_add_slave_domain", $domain_id); |
933
|
|
|
$this->catchall_set($domain_id, '@' . $target_domain); |
934
|
|
|
return true; |
935
|
|
|
} |
936
|
|
|
|
937
|
|
|
/* ----------------------------------------------------------------- */ |
938
|
|
|
|
939
|
|
|
/** hook function called by AlternC when a domain is created for |
940
|
|
|
* the current user account |
941
|
|
|
* This function create a postmaster mail which is an alias to LOGIN @ FQDN |
942
|
|
|
* wich is a dynamic alias to the alternc's account mail |
943
|
|
|
* @param string $domain_id Domain that has just been created |
944
|
|
|
* @access private |
945
|
|
|
*/ |
946
|
|
|
function hook_dom_add_mx_domain($domain_id) { |
947
|
|
|
global $err, $mem, $db; |
948
|
|
|
$err->log("mail", "hook_dom_add_mx_domain", $domain_id); |
949
|
|
|
|
950
|
|
|
$db->query("SELECT value FROM variable where name='mailname_bounce';"); |
951
|
|
|
if (!$db->next_record()) { |
952
|
|
|
$err->raise("mail", _("Problem: can't create default bounce mail")); |
953
|
|
|
return false; |
954
|
|
|
} |
955
|
|
|
$mailname = $db->f("value"); |
956
|
|
|
// set spf & dmarc for this domain |
957
|
|
|
$db->query("SELECT domaine FROM domaines WHERE id=$domain_id;"); |
958
|
|
|
if ($db->next_record()) { |
959
|
|
|
if ($spf = variable_get("default_spf_value")) { |
960
|
|
|
$this->set_dns_spf($db->Record["domaine"], $spf); |
961
|
|
|
} |
962
|
|
|
if ($dmarc = variable_get("default_dmarc_value")) { |
963
|
|
|
$this->set_dns_dmarc($db->Record["domaine"], $dmarc); |
964
|
|
|
} |
965
|
|
|
} |
966
|
|
|
return $this->create_alias($domain_id, 'postmaster', $mem->user['login'] . '@' . $mailname); |
967
|
|
|
} |
968
|
|
|
|
969
|
|
|
/* ----------------------------------------------------------------- */ |
970
|
|
|
|
971
|
|
|
/** hook function called by variables when a variable is changed |
972
|
|
|
* @access private |
973
|
|
|
*/ |
974
|
|
|
function hook_variable_set($name, $old, $new) { |
975
|
|
|
global $err, $db; |
976
|
|
|
$err->log("mail", "hook_variable_set($name,$old,$new)"); |
977
|
|
|
|
978
|
|
|
if ($name == "default_spf_value") { |
979
|
|
|
$new = trim($new); |
980
|
|
|
$old = trim($old); |
981
|
|
|
$db->query("SELECT domaine,login,compte FROM domaines, membres WHERE gesdns=1 AND gesmx=1 and membres.uid=domaines.compte;"); |
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
|
|
|
while ($db->next_record()) $res[]=$db->Record; |
993
|
|
|
foreach ($res as $record) { |
994
|
|
|
$this->set_dns_dmarc($record["domaine"], $new, $old, $record["compte"], $record["login"]); |
995
|
|
|
} |
996
|
|
|
} |
997
|
|
|
} |
998
|
|
|
|
999
|
|
|
/* ----------------------------------------------------------------- */ |
1000
|
|
|
|
1001
|
|
|
/** Set or UPDATE the DNS record for the domain $dom(str) to be $spf |
1002
|
|
|
* account's login is current and if not it's $login. |
1003
|
|
|
* don't change spf if current value is not $old |
1004
|
|
|
* @access private |
1005
|
|
|
*/ |
1006
|
|
|
function set_dns_spf($domain, $spf, $previous = -1, $uid = -1, $login = -1) { |
1007
|
|
|
global $db, $cuid, $mem; |
1008
|
|
|
// defaults |
1009
|
|
|
if ($uid === -1) { |
1010
|
|
|
$uid = intval($cuid); |
1011
|
|
|
} else { |
1012
|
|
|
$uid = intval($uid); |
1013
|
|
|
} |
1014
|
|
|
if ($login === -1) { |
1015
|
|
|
$login = $mem->user["login"]; |
1016
|
|
|
} |
1017
|
|
|
// Search for the record in sub_domaines table |
1018
|
|
|
$db->query("SELECT * FROM sub_domaines WHERE compte=$uid AND domaine='" . addslashes($domain) . "' AND sub='' AND type='txt' AND valeur LIKE 'v=spf1 %' AND web_action!='DELETE';"); |
1019
|
|
|
if ($db->next_record()) { |
1020
|
|
|
if ($previous !== -1 && $db->Record["valeur"] == "v=spf1 " . $spf) { |
1021
|
|
|
return; // skip, no change asked. |
1022
|
|
|
} |
1023
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id='" . $db->Record["id"] . "';"); |
1024
|
|
|
} |
1025
|
|
|
$db->query("INSERT INTO sub_domaines SET compte=$uid, domaine='" . addslashes($domain) . "', sub='', type='txt', valeur='" . addslashes("v=spf1 " . $spf) . "', web_action='UPDATE';"); |
1026
|
|
|
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine='" . addslashes($domain) . "';"); |
1027
|
|
|
} |
1028
|
|
|
|
1029
|
|
|
/* ----------------------------------------------------------------- */ |
1030
|
|
|
|
1031
|
|
|
/** Set or UPDATE the DNS record for the domain $dom(str) to be $dmarc |
1032
|
|
|
* account's login is current and if not it's $login. |
1033
|
|
|
* don't change dmarc if current value is not $old |
1034
|
|
|
* @access private |
1035
|
|
|
*/ |
1036
|
|
|
function set_dns_dmarc($domain, $dmarc, $previous = -1, $uid = -1, $login = -1) { |
1037
|
|
|
global $db, $cuid, $mem, $L_FQDN; |
1038
|
|
|
// defaults |
1039
|
|
|
if ($uid === -1) { |
1040
|
|
|
$uid = intval($cuid); |
1041
|
|
|
} else { |
1042
|
|
|
$uid = intval($uid); |
1043
|
|
|
} |
1044
|
|
|
if ($login === -1) { |
1045
|
|
|
$login = $mem->user["login"]; |
1046
|
|
|
} |
1047
|
|
|
$dmarc = str_replace("%%ADMINMAIL%%", "admin@" . $L_FQDN, $dmarc); |
1048
|
|
|
$dmarc = str_replace("%%USERMAIL%%", $login . "@" . $L_FQDN, $dmarc); |
1049
|
|
|
|
1050
|
|
|
// Search for the record in sub_domaines table |
1051
|
|
|
$db->query("SELECT * FROM sub_domaines WHERE compte=$uid AND domaine='" . addslashes($domain) . "' AND sub='_dmarc' AND type='txt' AND valeur LIKE 'v=dmarc1;%' AND web_action!='DELETE';"); |
1052
|
|
|
if ($db->next_record()) { |
1053
|
|
|
if ($previous !== -1 && $db->Record["valeur"] == "v=dmarc1;" . $dmarc) { |
1054
|
|
|
return; // skip, no change asked. |
1055
|
|
|
} |
1056
|
|
|
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE id='" . $db->Record["id"] . "';"); |
1057
|
|
|
} |
1058
|
|
|
$db->query("INSERT INTO sub_domaines SET compte=$uid, domaine='" . addslashes($domain) . "', sub='_dmarc', type='txt', valeur='" . addslashes("v=dmarc1;" . $dmarc) . "', web_action='UPDATE';"); |
1059
|
|
|
$db->query("UPDATE domaines SET dns_action='UPDATE' WHERE domaine='" . addslashes($domain) . "';"); |
1060
|
|
|
} |
1061
|
|
|
|
1062
|
|
|
/* ----------------------------------------------------------------- */ |
1063
|
|
|
|
1064
|
|
|
/** hook function called by AlternC-upnp to know which open |
1065
|
|
|
* tcp or udp ports this class requires or suggests |
1066
|
|
|
* @return array a key => value list of port protocol name mandatory values |
1067
|
|
|
* @access private |
1068
|
|
|
*/ |
1069
|
|
|
function hook_upnp_list() { |
1070
|
|
|
return array( |
1071
|
|
|
"imap" => array("port" => 143, "protocol" => "tcp", "mandatory" => 1), |
1072
|
|
|
"imaps" => array("port" => 993, "protocol" => "tcp", "mandatory" => 1), |
1073
|
|
|
"pop" => array("port" => 110, "protocol" => "tcp", "mandatory" => 1), |
1074
|
|
|
"pops" => array("port" => 995, "protocol" => "tcp", "mandatory" => 1), |
1075
|
|
|
"smtp" => array("port" => 25, "protocol" => "tcp", "mandatory" => 1), |
1076
|
|
|
"sieve" => array("port" => 2000, "protocol" => "tcp", "mandatory" => 1), |
1077
|
|
|
"submission" => array("port" => 587, "protocol" => "tcp", "mandatory" => 0), |
1078
|
|
|
); |
1079
|
|
|
} |
1080
|
|
|
|
1081
|
|
|
} |
1082
|
|
|
|
1083
|
|
|
/* Class m_mail */ |
1084
|
|
|
|
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: