@@ -64,6 +64,17 @@ def _make_request(self, endpoint, data=None, method='POST'):
6464                text  =  response .text .strip ()
6565                print (f"Raw response: { text [:500 ]}  ..." )  # First 500 chars for debugging 
6666
67+                 # Check if we got HTML instead of API data 
68+                 if  text .startswith ('<!DOCTYPE html' ) or  text .startswith ('<html' ):
69+                     print (f"ERROR: Received HTML response instead of API data" )
70+                     print (f"This usually means the API endpoint doesn't exist or authentication failed" )
71+                     return  None 
72+ 
73+                 # Check for empty response 
74+                 if  not  text :
75+                     print (f"ERROR: Empty response from DirectAdmin API" )
76+                     return  None 
77+ 
6778                # Parse response into dictionary first 
6879                result  =  {}
6980
@@ -142,52 +153,107 @@ def _make_request(self, endpoint, data=None, method='POST'):
142153    def  test_connection (self ):
143154        """Test the connection to DirectAdmin""" 
144155        try :
156+             print (f"\n === Testing Connection to { self .server }   ===" )
157+             print (f"Username: { self .username }  " )
158+             print (f"Domain: { self .domain }  " )
159+             
145160            # Try CMD_API_SHOW_DOMAINS first 
146161            endpoint  =  '/CMD_API_SHOW_DOMAINS' 
147162            response  =  self ._make_request (endpoint , method = 'GET' )
148163
149-             if  response :
164+             if  response   is   not   None :
150165                if  isinstance (response , dict ):
151166                    # Check if our domain is in the list 
152167                    if  self .domain :
153168                        # DirectAdmin might return domains in various formats 
154169                        domain_list  =  []
155170                        for  key , value  in  response .items ():
156171                            if  'domain'  in  key .lower () or  key .startswith ('list' ):
157-                                 domain_list .append (value )
158-                             elif  '.'  in  key :  # Might be domain name as key 
172+                                 if  isinstance (value , list ):
173+                                     domain_list .extend (value )
174+                                 else :
175+                                     domain_list .append (value )
176+                             elif  '.'  in  key  and  not  key .startswith ('<' ):  # Might be domain name as key, but not HTML 
159177                                domain_list .append (key )
160178
179+                         print (f"Found domains: { domain_list }  " )
161180                        if  self .domain  in  domain_list :
162181                            return  True , f"Successfully connected. Domain { self .domain }   found." 
163182                        else :
164-                             return  True , f"Connected, but domain { self .domain }   not found in account." 
183+                             return  True , f"Connected, but domain { self .domain }   not found in account. Available domains:  { ', ' . join ( domain_list [: 3 ]) } { '...'   if   len ( domain_list )  >   3   else   '' }  " 
165184                    else :
166185                        return  True , "Successfully connected to DirectAdmin." 
167186                else :
168187                    return  True , "Successfully connected to DirectAdmin." 
188+             else :
189+                 print ("CMD_API_SHOW_DOMAINS returned None (likely HTML response)" )
169190
170191            # If that fails, try a simpler endpoint 
192+             print ("Trying CMD_API_SHOW_USER_CONFIG..." )
171193            endpoint  =  '/CMD_API_SHOW_USER_CONFIG' 
172194            response  =  self ._make_request (endpoint , method = 'GET' )
173195
174-             if  response :
196+             if  response   is   not   None :
175197                return  True , "Successfully connected to DirectAdmin." 
198+             else :
199+                 print ("CMD_API_SHOW_USER_CONFIG also returned None" )
176200
177-             return  False , "Failed to connect. Please  check your credentials." 
201+             return  False , "Failed to connect. Server returned HTML instead of API data - please  check your DirectAdmin URL,  credentials, and API access ." 
178202
179203        except  Exception  as  e :
180204            import  traceback 
181-             print (f"Connection error: { str (e )}  " )
205+             error_msg  =  str (e )
206+             print (f"Connection test exception: { error_msg }  " )
182207            traceback .print_exc ()
183-             return  False , "Connection error: Unable to connect to DirectAdmin." 
208+             
209+             # Provide more specific error messages 
210+             if  'timeout'  in  error_msg .lower ():
211+                 return  False , "Connection timed out. Please check your DirectAdmin server URL and network connection." 
212+             elif  'connection'  in  error_msg .lower ():
213+                 return  False , "Unable to connect to DirectAdmin server. Please verify the server URL and credentials." 
214+             elif  'ssl'  in  error_msg .lower () or  'certificate'  in  error_msg .lower ():
215+                 return  False , "SSL certificate error. Try using HTTP instead of HTTPS." 
216+             else :
217+                 return  False , f"Connection error: { error_msg }  " 
218+ 
219+     def  validate_domain_access (self ):
220+         """Check if the current domain is accessible via the API""" 
221+         try :
222+             print (f"\n === Validating Domain Access for { self .domain }   ===" )
223+             
224+             # Try to get domain list to verify access 
225+             endpoint  =  '/CMD_API_SHOW_DOMAINS' 
226+             response  =  self ._make_request (endpoint , method = 'GET' )
227+             
228+             if  response  and  isinstance (response , dict ):
229+                 domain_list  =  []
230+                 for  key , value  in  response .items ():
231+                     if  'domain'  in  key .lower () or  key .startswith ('list' ):
232+                         domain_list .append (value )
233+                     elif  '.'  in  key  and  not  key .startswith ('<' ):  # Might be domain name as key, but not HTML 
234+                         domain_list .append (key )
235+                 
236+                 if  self .domain  in  domain_list :
237+                     print (f"✓ Domain { self .domain }   found in account" )
238+                     return  True , f"Domain { self .domain }   is accessible" 
239+                 else :
240+                     print (f"✗ Domain { self .domain }   not found in account" )
241+                     print (f"Available domains: { domain_list }  " )
242+                     return  False , f"Domain { self .domain }   not found in DirectAdmin account" 
243+             
244+             print ("Could not verify domain access - no domain list returned" )
245+             return  False , "Unable to verify domain access" 
246+             
247+         except  Exception  as  e :
248+             print (f"Error validating domain access: { e }  " )
249+             return  False , f"Error validating domain: { str (e )}  " 
184250
185251    def  get_email_accounts (self ):
186252        """Get all email accounts for the domain""" 
187253        try :
188254            print (f"\n === Getting Email Accounts for { self .domain }   ===" )
189255
190-             # Try multiple  endpoints 
256+             # Try API  endpoints only  
191257            endpoints  =  [
192258                ('/CMD_API_POP' , {'action' : 'list' , 'domain' : self .domain }),
193259                ('/CMD_API_POP' , {'domain' : self .domain }),
@@ -202,7 +268,11 @@ def get_email_accounts(self):
202268                    break 
203269
204270            if  response  is  None :
205-                 print ("No response from any email endpoint" )
271+                 print ("No valid response from any email accounts endpoint" )
272+                 print ("This could mean:" )
273+                 print ("- The domain doesn't exist in DirectAdmin" )
274+                 print ("- API user doesn't have permission for this domain" )
275+                 print ("- DirectAdmin API is not properly configured" )
206276                return  []
207277
208278            print (f"Raw response type: { type (response )}  " )
@@ -267,15 +337,29 @@ def get_email_accounts(self):
267337                    elif  line  and  not  line .startswith ('error' ):
268338                        accounts .append (f"{ line }  @{ self .domain }  " )
269339
270-             # Ensure all accounts have domain part 
340+             # Ensure all accounts have domain part and filter out invalid entries  
271341            processed_accounts  =  []
272342            for  account  in  accounts :
273343                if  account :  # Skip empty strings 
344+                     # Skip entries that look like HTML 
345+                     if  account .startswith ('<' ) or  '"'  in  account  or  account .startswith (':root' ):
346+                         print (f"Skipping invalid account that looks like HTML: { account }  " )
347+                         continue 
348+                     
349+                     # Validate email format 
350+                     import  re 
274351                    if  '@'  not  in   account :
275-                         # Add domain if missing 
276-                         processed_accounts .append (f"{ account }  @{ self .domain }  " )
352+                         # Validate username part before adding domain 
353+                         if  re .match (r'^[a-zA-Z0-9._-]+$' , account ):
354+                             processed_accounts .append (f"{ account }  @{ self .domain }  " )
355+                         else :
356+                             print (f"Skipping invalid username: { account }  " )
277357                    else :
278-                         processed_accounts .append (account )
358+                         # Validate full email 
359+                         if  re .match (r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' , account ):
360+                             processed_accounts .append (account )
361+                         else :
362+                             print (f"Skipping invalid email: { account }  " )
279363
280364            # Remove duplicates and filter out API user 
281365            processed_accounts  =  list (set (processed_accounts ))
@@ -298,11 +382,10 @@ def get_forwarders(self):
298382        try :
299383            print (f"\n === Getting Forwarders for { self .domain }   ===" )
300384
301-             # Try multiple endpoint variations  
385+             # Try API endpoints only (avoid web interface endpoints)  
302386            endpoints  =  [
303387                ('/CMD_API_EMAIL_FORWARDERS' , {'domain' : self .domain , 'action' : 'list' }),
304388                ('/CMD_API_EMAIL_FORWARDERS' , {'domain' : self .domain }),
305-                 ('/CMD_EMAIL_FORWARDERS' , {'domain' : self .domain }),
306389            ]
307390
308391            response  =  None 
@@ -312,17 +395,21 @@ def get_forwarders(self):
312395                # Try GET first 
313396                response  =  self ._make_request (endpoint , params , method = 'GET' )
314397                if  response :
315-                     print (f"Got response with GET" )
398+                     print (f"Got valid  response with GET" )
316399                    break 
317400
318401                # Try POST 
319402                response  =  self ._make_request (endpoint , params , method = 'POST' )
320403                if  response :
321-                     print (f"Got response with POST" )
404+                     print (f"Got valid  response with POST" )
322405                    break 
323406
324407            if  response  is  None :
325-                 print ("ERROR: No response from any forwarders endpoint!" )
408+                 print ("ERROR: No valid response from any API endpoint!" )
409+                 print ("This could mean:" )
410+                 print ("- The domain doesn't exist in DirectAdmin" )
411+                 print ("- API user doesn't have permission for this domain" )
412+                 print ("- DirectAdmin API is not properly configured" )
326413                return  []
327414
328415            print (f"\n === FORWARDERS RAW RESPONSE ===" )
@@ -377,6 +464,18 @@ def get_forwarders(self):
377464                        if  key .startswith ('error' ) or  key  ==  'domain' :
378465                            continue 
379466
467+                         # Skip invalid keys that look like HTML 
468+                         if  key .startswith ('<' ) or  '"'  in  key  or  key .startswith (':root' ):
469+                             print (f"Skipping invalid key that looks like HTML: { key }  " )
470+                             continue 
471+ 
472+                         # Validate that the key looks like a valid email username 
473+                         # Allow alphanumeric, dots, hyphens, underscores 
474+                         import  re 
475+                         if  not  re .match (r'^[a-zA-Z0-9._-]+$' , key ):
476+                             print (f"Skipping invalid username: { key }  " )
477+                             continue 
478+ 
380479                        # IMPORTANT: Accept ALL non-empty values as valid destinations 
381480                        if  value :
382481                            # Key is the username, value is the destination 
0 commit comments