11<?php
2-
32/**
43 * @author Robin Appelman <icewind@owncloud.com>
54 * @author Jonas Sulzer <jonas@violoncello.ch>
87 * later.
98 * See the COPYING-README file.
109 */
10+
1111namespace OCA \UserExternal ;
1212
13+ use OCP \ILogger ;
14+
1315/**
1416 * User authentication against an IMAP mail server
15- *
16- * @category Apps
17- * @package UserExternal
18- * @author Robin Appelman <icewind@owncloud.com>
19- * @license http://www.gnu.org/licenses/agpl AGPL
20- * @link http://github.com/owncloud/apps
2117 */
2218class IMAP extends Base {
23- private $ mailbox ;
24- private $ port ;
25- private $ sslmode ;
26- private $ domain ;
27- private $ stripeDomain ;
28- private $ groupDomain ;
19+ private string $ mailbox ;
20+ private int $ port ;
21+ private ? string $ sslmode ;
22+ private string $ domain ;
23+ private bool $ stripeDomain ;
24+ private bool $ groupDomain ;
2925
3026 /**
31- * Create new IMAP authentication provider
32- *
3327 * @param string $mailbox IMAP server domain/IP
34- * @param int $port IMAP server $ port
35- * @param string $sslmode
36- * @param string $domain If provided, loging will be restricted to this domain
37- * @param boolean $stripeDomain (whether to stripe the domain part from the username or not)
38- * @param boolean $groupDomain (whether to add the usere to a group corresponding to the domain of the address)
28+ * @param int|null $port IMAP server port
29+ * @param string|null $sslmode ssl|tls|null
30+ * @param string|null $domain If provided, login will be restricted to this domain
31+ * @param bool $stripeDomain Whether to strip the domain part from the username
32+ * @param bool $groupDomain Whether to add the user to a group matching the email domain
3933 */
40- public function __construct ($ mailbox , $ port = null , $ sslmode = null , $ domain = null , $ stripeDomain = true , $ groupDomain = false ) {
34+ public function __construct (
35+ string $ mailbox ,
36+ ?int $ port = null ,
37+ ?string $ sslmode = null ,
38+ ?string $ domain = null ,
39+ bool $ stripeDomain = true ,
40+ bool $ groupDomain = false
41+ ) {
4142 parent ::__construct ($ mailbox );
4243 $ this ->mailbox = $ mailbox ;
43- $ this ->port = $ port === null ? 143 : $ port ;
44+ $ this ->port = $ port ?? 143 ;
4445 $ this ->sslmode = $ sslmode ;
45- $ this ->domain = $ domain === null ? '' : $ domain ;
46+ $ this ->domain = $ domain ?? '' ;
4647 $ this ->stripeDomain = $ stripeDomain ;
4748 $ this ->groupDomain = $ groupDomain ;
4849 }
4950
51+ private function logger (): ILogger {
52+ /** @var ILogger $logger */
53+ $ logger = \OC ::$ server ->get (ILogger::class);
54+ return $ logger ;
55+ }
56+
5057 /**
5158 * Check if the password is correct without logging in the user
5259 *
5360 * @param string $uid The username
5461 * @param string $password The password
55- *
56- * @return true/false
62+ * @return string|false
5763 */
5864 public function checkPassword ($ uid , $ password ) {
59- $ uid = $ this ->resolveUid ($ uid );
65+ if ($ password === '' ) {
66+ return false ;
67+ }
6068
6169 // Replace escaped @ symbol in uid (which is a mail address)
62- // but only if there is no @ symbol and if there is a %40 inside the uid
63- if (!(strpos ($ uid , '@ ' ) !== false ) && (strpos ($ uid , '%40 ' ) !== false )) {
70+ if (strpos ($ uid , '@ ' ) === false && strpos ($ uid , '%40 ' ) !== false ) {
6471 $ uid = str_replace ('%40 ' , '@ ' , $ uid );
6572 }
6673
6774 $ pieces = explode ('@ ' , $ uid );
75+
6876 if ($ this ->domain !== '' ) {
6977 if (count ($ pieces ) === 1 ) {
7078 $ username = $ uid . '@ ' . $ this ->domain ;
@@ -74,8 +82,8 @@ public function checkPassword($uid, $password) {
7482 $ uid = $ pieces [0 ];
7583 }
7684 } else {
77- $ this ->logger ->error (
78- 'ERROR: User has a wrong domain! Expecting : ' . $ this ->domain ,
85+ $ this ->logger () ->error (
86+ 'User has a wrong domain. Expected : ' . $ this ->domain ,
7987 ['app ' => 'user_external ' ]
8088 );
8189 return false ;
@@ -85,20 +93,31 @@ public function checkPassword($uid, $password) {
8593 }
8694
8795 $ groups = [];
88- if (( count ($ pieces ) > 1 ) && $ this ->groupDomain && $ pieces [1 ]) {
96+ if (count ($ pieces ) > 1 && $ this ->groupDomain && ! empty ( $ pieces [1 ]) ) {
8997 $ groups [] = $ pieces [1 ];
9098 }
9199
92100 $ protocol = ($ this ->sslmode === 'ssl ' ) ? 'imaps ' : 'imap ' ;
93- $ url = "{$ protocol }:// {$ this ->mailbox }: {$ this ->port }" ;
101+ $ url = sprintf ('%s://%s:%d ' , $ protocol , $ this ->mailbox , $ this ->port );
102+
94103 $ ch = curl_init ();
104+ if ($ ch === false ) {
105+ $ this ->logger ()->error (
106+ 'Could not initialize curl for IMAP authentication. ' ,
107+ ['app ' => 'user_external ' ]
108+ );
109+ return false ;
110+ }
111+
95112 if ($ this ->sslmode === 'tls ' ) {
96113 curl_setopt ($ ch , CURLOPT_USE_SSL , CURLUSESSL_ALL );
97114 }
115+
98116 curl_setopt ($ ch , CURLOPT_URL , $ url );
99117 curl_setopt ($ ch , CURLOPT_RETURNTRANSFER , true );
100118 curl_setopt ($ ch , CURLOPT_USERPWD , $ username . ': ' . $ password );
101119 curl_setopt ($ ch , CURLOPT_CONNECTTIMEOUT , 10 );
120+ curl_setopt ($ ch , CURLOPT_TIMEOUT , 15 );
102121 curl_setopt ($ ch , CURLOPT_CUSTOMREQUEST , 'CAPABILITY ' );
103122
104123 curl_exec ($ ch );
@@ -109,35 +128,34 @@ public function checkPassword($uid, $password) {
109128 $ uid = mb_strtolower ($ uid );
110129 $ this ->storeUser ($ uid , $ groups );
111130 return $ uid ;
112- } elseif ($ errorcode === CURLE_COULDNT_CONNECT
113- || $ errorcode === CURLE_SSL_CONNECT_ERROR
114- || $ errorcode === 28 ) {
115- # This is not defined in PHP-8.x
116- # 28: CURLE_OPERATION_TIMEDOUT
117- $ this ->logger ->error (
118- 'ERROR: Could not connect to imap server via curl: ' . curl_strerror ($ errorcode ),
131+ }
132+
133+ if (
134+ $ errorcode === CURLE_COULDNT_CONNECT ||
135+ $ errorcode === CURLE_SSL_CONNECT_ERROR ||
136+ $ errorcode === CURLE_OPERATION_TIMEDOUT
137+ ) {
138+ $ this ->logger ()->error (
139+ 'Could not connect to IMAP server via curl: ' . curl_strerror ($ errorcode ),
119140 ['app ' => 'user_external ' ]
120141 );
121- } elseif ($ errorcode === 9
122- || $ errorcode === 67
123- || $ errorcode === 94 ) {
124- # These are not defined in PHP-8.x
125- # 9: CURLE_REMOTE_ACCESS_DENIED
126- # 67: CURLE_LOGIN_DENIED
127- # 94: CURLE_AUTH_ERROR)
128- $ this ->logger ->error (
129- 'ERROR: IMAP Login failed via curl: ' . curl_strerror ($ errorcode ),
142+ } elseif (
143+ $ errorcode === CURLE_REMOTE_ACCESS_DENIED ||
144+ $ errorcode === CURLE_LOGIN_DENIED ||
145+ (defined ('CURLE_AUTH_ERROR ' ) && $ errorcode === CURLE_AUTH_ERROR )
146+ ) {
147+ $ this ->logger ()->warning (
148+ 'IMAP login failed via curl: ' . curl_strerror ($ errorcode ),
130149 ['app ' => 'user_external ' ]
131150 );
132151 } else {
133- $ this ->logger ->error (
134- 'ERROR: IMAP server returned an error: ' . $ errorcode . ' / ' . curl_strerror ($ errorcode ),
152+ $ this ->logger () ->error (
153+ 'IMAP server returned an error: ' . $ errorcode . ' / ' . curl_strerror ($ errorcode ),
135154 ['app ' => 'user_external ' ]
136155 );
137156 }
138157
139158 curl_close ($ ch );
140-
141159 return false ;
142160 }
143161}
0 commit comments