Skip to content

Commit 2f21ece

Browse files
author
Till Brehm
committed
Merge branch 'undefined' into 'master'
Example csv import email script. See merge request ispconfig/ispconfig3!910
2 parents cc7931d + f2df6e1 commit 2f21ece

File tree

1 file changed

+305
-0
lines changed

1 file changed

+305
-0
lines changed
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/usr/bin/php
2+
<?php
3+
#
4+
# ispc-import-csv-email.php: import email accounts from csv into ispconfig
5+
#
6+
7+
# ISPConfig remote api params
8+
$remote_user = 'importer';
9+
$remote_pass = 'apipassword';
10+
$remote_url = 'https://yourserver.com:8080/remote/json.php';
11+
12+
# CSV file
13+
$csv_file="/home/migrations/test.csv";
14+
15+
16+
# csv file format (first line is header names, column order does not matter):
17+
#
18+
# "email","password","quota","name","cc","bcc","move_junk","autoresponder","autoresponder_text","virus_lover","spam_lover"
19+
# "api_standard@apitest.com","insecure","150","API User Insert: Standard Mailbox","","","yes","no","this is vacation text, although vacation is not enabled","N","N"
20+
# "api_no_spambox@apitest.com","insecure","150","API User Insert: Mailbox with move_junk off","","","no","no","this is vacation text, although vacation is not enabled","N","N"
21+
# "api_vacation@apitest.com","insecure","150","API User Insert: Mailbox with vacation","","","yes","yes","this is vacation text, with vacation enabled","N","N"
22+
# "api_forward@apitest.com","insecure","150","API User Insert: Mail Forward","your-test-addr@test.com","","no","no","this is vacation text, although vacation is not enabled","N","N"
23+
# "api_both1@apitest.com","insecure","150","API User Insert: Mailbox with forward via cc","your-test-addr@test.com","","yes","no","this is vacation text, although vacation is not enabled","N","N"
24+
# "api_both2@apitest.com","insecure","150","API User Insert: Mailbox with forward via bcc","","your-test-addr@test.com","yes","no","this is vacation text, although vacation is not enabled","N","N"
25+
# "api_virus_lover@apitest.com","insecure","150","API User Insert: Mailbox with virus_lover","","","yes","no","","Y","N"
26+
# "api_spam_lover@apitest.com","insecure","150","API User Insert: Mailbox with spam_lover","","","yes","no","","N","Y"
27+
# "api_both_lover@apitest.com","insecure","150","API User Insert: Mailbox with virus_lover and spam_lover","","","yes","no","","Y","Y"
28+
29+
30+
/**
31+
* Call REST endpoint.
32+
*/
33+
function restCall( $method, $data ) {
34+
global $remote_url;
35+
36+
if(!is_array($data)) return false;
37+
$json = json_encode($data);
38+
39+
$curl = curl_init();
40+
curl_setopt($curl, CURLOPT_POST, 1);
41+
42+
if($data) curl_setopt($curl, CURLOPT_POSTFIELDS, $json);
43+
44+
// needed for self-signed cert
45+
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
46+
//curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
47+
// end of needed for self-signed cert
48+
49+
curl_setopt($curl, CURLOPT_URL, $remote_url . '?' . $method);
50+
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
51+
52+
$result = curl_exec($curl);
53+
curl_close($curl);
54+
55+
return $result;
56+
}
57+
58+
$session_id = '';
59+
60+
/**
61+
* Logout of active session and die with message.
62+
*/
63+
function session_die( $msg ) {
64+
global $session_id;
65+
66+
if ( isset( $session_id ) && $session_id ) {
67+
$result = restCall( 'logout', [ 'session_id' => $session_id ] );
68+
$result || die( "$msg\nAdditionally, could not get logout result, session id $session_id may now be abandoned.\n" );
69+
}
70+
71+
die( "$msg\n" );
72+
}
73+
74+
/**
75+
* Make api call, checking for errors and return 'response' from the decoded data. Opens session if required.
76+
*/
77+
function apiCall( ...$args ) {
78+
global $remote_user, $remote_pass, $session_id;
79+
80+
// login to remote api and obtain session id if needed
81+
if ( ! ( isset( $session_id ) && $session_id ) ) {
82+
$result = restCall( 'login', [ 'username' => $remote_user, 'password' => $remote_pass, 'client_login' => false, ] );
83+
84+
if ( $result ) {
85+
$result = json_decode( $result, true );
86+
if ( ! $result ) {
87+
die( "Error: unable to login to remote api (json_decode failed)\n" );
88+
}
89+
90+
if ( isset( $result['response'] ) ) {
91+
$session_id = $result['response'];
92+
} else {
93+
die( "Error: failed to obtain session id from remote api login\n" );
94+
}
95+
}
96+
}
97+
98+
$rest_args = func_get_args();
99+
$method = array_shift( $rest_args );
100+
101+
$result = restCall( $method, array_merge( [ 'session_id' => $session_id, ], ...$rest_args ) );
102+
103+
if ( $result ) $data = json_decode( $result, true );
104+
else session_die( "Could not get $method result" );
105+
106+
if ( isset( $data['code'] ) && 'ok' != $data['code'] ) {
107+
$msg = "$method returned " . $data['code']
108+
. ( isset( $data['message'] ) ? ": " . $data['message'] . "\n" : "\n" );
109+
session_die( $msg );
110+
}
111+
112+
return ( isset( $data['response'] ) ? $data['response'] : $data );
113+
}
114+
115+
if ( ! file_exists( "$csv_file" ) ) {
116+
die( "CSV file ($csv_file) not found.\n" );
117+
}
118+
119+
// get all mail policies
120+
$mail_policies = apiCall( 'mail_policy_get', [ 'primary_id' => [] ] );
121+
if ( ! $mail_policies ) {
122+
session_die( "Error: could not look up mail policies\n" );
123+
}
124+
125+
// get all spamfilter_user settings
126+
$mail_spamfilter_users = apiCall( 'mail_spamfilter_user_get', [ 'primary_id' => [] ] );
127+
if ( ! $mail_spamfilter_users ) {
128+
session_die( "Error: could not look up mail spamfilter users\n" );
129+
}
130+
131+
$mail_domains = [];
132+
133+
// Read csv file, map rows and loop through them
134+
$rows = array_map( 'str_getcsv', file( $csv_file ) );
135+
$header = array_shift( $rows );
136+
$email_idx = array_search( 'email', $header );
137+
if ( $email_idx === FALSE ) {
138+
session_die( "Error in csv file: 'email' field not found.\n" );
139+
}
140+
$csv = [];
141+
foreach( $rows as $row ) {
142+
$email = $row[$email_idx];
143+
$domain = substr( $email, strpos( $email, '@' ) + 1 );
144+
145+
if ( is_array( $row ) && count( $header ) == count( $row ) ) {
146+
$csv[$email] = array_combine( $header, $row );
147+
} else {
148+
print "Error in csv file: problem parsing email '$email'\n";
149+
continue;
150+
}
151+
152+
// look up mail_domain record for this domain
153+
if ( ! isset( $mail_domains[$domain] ) ) {
154+
$data = apiCall( 'mail_domain_get_by_domain', [ 'domain' => $domain ] );
155+
156+
if ( is_array( $data ) && isset( $data[0] ) ) {
157+
158+
// unset these (large and don't need them)
159+
unset( $data[0]['dkim'] );
160+
unset( $data[0]['dkim_selector'] );
161+
unset( $data[0]['dkim_public'] );
162+
unset( $data[0]['dkim_private'] );
163+
164+
$mail_domains[$domain] = $data[0];
165+
166+
foreach ( $mail_spamfilter_users as $msu ) {
167+
if ( $msu['email'] == "@$domain" && $msu['server_id'] == $mail_domains[$domain]['server_id'] ) {
168+
$mail_domains[$domain]['spamfilter_policy_id'] = $msu['policy_id'];
169+
}
170+
}
171+
} else {
172+
$mail_domains[$domain] = [ 'domain_id' => -1, 'domain' => $domain, ];
173+
print( "Error: mail_domain $domain does not exist, you must create it first.\n" );
174+
}
175+
}
176+
}
177+
178+
// dump manually created account to compare values
179+
//$data = apiCall( 'mail_user_get', [ 'primary_id' => [ 'email' => 'manual@apitest.com' ] ] );
180+
//var_dump( $data, true );
181+
182+
foreach ( $csv as $record ) {
183+
$email = $record['email'];
184+
$addr = substr( $email, 0, strpos( $email, '@' ) );
185+
$domain = substr( $email, strpos( $email, '@' ) + 1 );
186+
187+
// ensure we have mail_domain info
188+
if ( ! isset( $mail_domains[$domain] ) || -1 == $mail_domains[$domain]['domain_id'] ) {
189+
print "Config for domain $domain not available, cannot add email $email.\n";
190+
continue;
191+
}
192+
193+
// skip if email already exists
194+
$data = apiCall( 'mail_user_get', [ 'primary_id' => [ 'email' => $email ] ] );
195+
if ( is_array( $data ) && isset( $data[0] ) && isset( $data[0]['mailuser_id'] ) ) {
196+
print "Email $email already exists, skipping.\n";
197+
continue;
198+
}
199+
200+
// get client_id for this sys_userid
201+
if ( isset( $mail_domains[$domain]['client_id'] ) ) {
202+
$client_id = $mail_domains[$domain]['client_id'];
203+
} else {
204+
$client_id = apiCall( 'client_get_id', [ 'sys_userid' => $mail_domains[$domain]['sys_userid'] ] );
205+
if ( ! $client_id ) {
206+
print "Error: unable to determine client_id for $domain (sys_userid is " . $mail_domains[$domain]['sys_userid'] . "),\n";
207+
print "cannot create mailbox for Email $email\n";
208+
continue;
209+
}
210+
$mail_domains[$domain]['client_id'] = $client_id;
211+
}
212+
213+
// mail_user_add parameters for this email
214+
$params = [ 'params' => [
215+
'server_id' => $mail_domains[$domain]['server_id'],
216+
'email' => $email,
217+
'login' => $email,
218+
'password' => $record['password'],
219+
'name' => $record['name'],
220+
'uid' => 5000,
221+
'gid' => 5000,
222+
'maildir' => "/var/vmail/$domain/$addr",
223+
'quota' => $record['quota'] * 1024 * 1024,
224+
'cc' => implode( ',', array_filter( [ $record['cc'], $record['bcc'] ] ) ),
225+
'homedir' => "/var/vmail/",
226+
'autoresponder' => ( preg_match( '/^y/i', $record['autoresponder'] ) ? 'y' : 'n' ),
227+
'autoresponder_start_date' => date( 'Y-m-d H:i:s' ),
228+
'autoresponder_end_date' => date( '2024-m-d H:i:s' ),
229+
'autoresponder_text' => $record['autoresponder_text'],
230+
'move_junk' => ( preg_match( '/^y/i', $record['move_junk'] ) ? 'y' : 'n' ),
231+
'custom_mailfilter' => "",
232+
'postfix' => 'y',
233+
'access' => 'y',
234+
// 'disableimap' => 'n',
235+
// 'disablepop3' => 'n',
236+
// 'disabledeliver' => 'n',
237+
// 'disablesmtp' => 'n',
238+
],
239+
];
240+
241+
// add mail user
242+
$data = apiCall( 'mail_user_add', [ 'client_id' => $client_id ], $params );
243+
244+
if ( ! $data ) {
245+
print "mail_user_add may have a problem inserting $email\n";
246+
continue;
247+
}
248+
249+
//$data = apiCall( 'mail_user_get', [ 'primary_id' => [ 'email' => $email ] ] );
250+
//var_dump( $data, true );
251+
252+
// determine mail policy
253+
$spam_lover = ( preg_match( '/^y/i', $record['move_junk'] ) ? $record['spam_lover'] : 'N' );
254+
$virus_lover = $record['virus_lover'];
255+
$spamfilter_policy_id = null;
256+
257+
// check domain's policy settings for bypass_spam_checks == 'N' and matching spam_lover/virus_lover,
258+
// if a match, we're done
259+
if ( isset( $mail_domains[$domain]['spamfilter_policy_id'] ) ) {
260+
foreach ( $mail_policies as $policy ) {
261+
if ( $policy['id'] == $mail_domains[$domain]['spamfilter_policy_id'] ) {
262+
if ( 'N' == $policy['bypass_spam_checks'] && $policy['spam_lover'] == $spam_lover && $policy['virus_lover'] == $virus_lover ) {
263+
$spamfilter_policy_id = $policy['id'];
264+
}
265+
}
266+
}
267+
}
268+
// if domain's policy doesn't match, loop through all policies to find a match and insert it
269+
if ( null === $spamfilter_policy_id ) {
270+
foreach ( $mail_policies as $policy ) {
271+
if ( 'Y' == $policy['bypass_spam_checks'] ) {
272+
continue;
273+
}
274+
if ( $policy['spam_lover'] == $spam_lover && $policy['virus_lover'] == $virus_lover ) {
275+
$spamfilter_policy_id = $policy['id'];
276+
277+
// mail_spamfilter_user entry for this user / policy_id
278+
$params = [ 'params' => [
279+
'server_id' => $mail_domains[$domain]['server_id'],
280+
'priority' => "10",
281+
'policy_id' => $policy['id'],
282+
'email' => $email,
283+
'fullname' => $email,
284+
'local' => "Y",
285+
],
286+
];
287+
288+
$data = apiCall( 'mail_spamfilter_user_add', [ 'client_id' => $client_id ], $params );
289+
290+
// either we inserted a spamfilter_user or it failed,
291+
// either way, on to the next email
292+
continue 2;
293+
}
294+
}
295+
}
296+
}
297+
298+
299+
// logout so session id is cleaned up
300+
if ( isset( $session_id ) && $session_id ) {
301+
$result = restCall( 'logout', [ 'session_id' => $session_id ] );
302+
$result || die( "Could not get logout result, session id $session_id may now be abandoned.\n" );
303+
}
304+
305+
exit();

0 commit comments

Comments
 (0)