#-------------------------------------------------------------------------------
# Name:         sfp_hunter
# Purpose:      Query hunter.io using their API.
#
# Author:      Steve Micallef <steve@binarypool.com>
#
# Created:     22/02/2017
# Copyright:   (c) Steve Micallef
# Licence:     GPL
#-------------------------------------------------------------------------------

import json
from sflib import SpiderFoot, SpiderFootPlugin, SpiderFootEvent

class sfp_hunter(SpiderFootPlugin):
    """Hunter.io:Footprint,Investigate,Passive:Search Engines:apikey:Check for e-mail addresses and names on hunter.io."""


    # Default options
    opts = { 
        "api_key": ""
    }

    # Option descriptions
    optdescs = {
        "api_key": "Hunter.io API key."
    }

    # Be sure to completely clear any class variables in setup()
    # or you run the risk of data persisting between scan runs.

    results = None
    errorState = False

    def setup(self, sfc, userOpts=dict()):
        self.sf = sfc
        self.results = self.tempStorage()
        self.errorState = False

        # Clear / reset any other class member variables here
        # or you risk them persisting between threads.

        for opt in list(userOpts.keys()):
            self.opts[opt] = userOpts[opt]

    # What events is this module interested in for input
    def watchedEvents(self):
        return [ "DOMAIN_NAME", "INTERNET_NAME" ]

    # What events this module produces
    def producedEvents(self):
        return [ "EMAILADDR", "RAW_RIR_DATA" ]

    def query(self, t):
        ret = None

        url = "https://api.hunter.io/v2/domain-search?domain=" + t + "&api_key=" + self.opts['api_key']

        res = self.sf.fetchUrl(url, timeout=self.opts['_fetchtimeout'], 
            useragent="SpiderFoot")

        if res['code'] == "404":
            return None

        if not res['content']:
            return None

        try:
            ret = json.loads(res['content'])['data']
        except Exception as e:
            self.sf.error("Error processing JSON response from hunter.io: " + str(e), False)
            return None

        return ret

    # Handle events sent to this module
    def handleEvent(self, event):
        eventName = event.eventType
        srcModuleName = event.module
        eventData = event.data

        if self.errorState:
            return None

        self.sf.debug("Received event, " + eventName + ", from " + srcModuleName)

       # Don't look up stuff twice
        if eventData in self.results:
            self.sf.debug("Skipping " + eventData + " as already mapped.")
            return None
        else:
            self.results[eventData] = True

        if self.opts['api_key'] == "":
            self.sf.error("You enabled sfp_hunter but did not set an API key!", False)
            self.errorState = True
            return None

        data = self.query(eventData)
        if data == None:
            return None

        if 'emails' not in data:
            return None

        for email in data['emails']:
            # Notify other modules of what you've found
            e = SpiderFootEvent("EMAILADDR", email['value'], self.__name__, event)
            self.notifyListeners(e)

            if 'first_name' in email and 'last_name' in email:
                if email['first_name'] != None and email['last_name'] != None:
                    n = email['first_name'] + " " + email['last_name']
                    e = SpiderFootEvent("RAW_RIR_DATA", "Possible full name: " + n, 
                                        self.__name__, event)
                    self.notifyListeners(e)


# End of sfp_hunter class

@@ -1553,16 +1553,36 @@
 (self, t
+, offset=0, limit=10
 ):%0A     
@@ -1699,16 +1699,84 @@
 pi_key'%5D
+ + %5C%0A              %22&offset=%22 + str(offset) + %22&limit=%22 + str(limit)
 %0A%0A      
@@ -2054,16 +2054,8 @@
 t'%5D)
-%5B'data'%5D
 %0A   
@@ -2986,16 +2986,24 @@
 ventData
+, 0, 100
 )%0A      
@@ -3011,142 +3011,523 @@
  if 
-data == None:%0A            return None%0A%0A        if 'emails' not in data:%0A            return None%0A%0A        for email in data%5B'emails'%5D:%0A
+not data:%0A            return None%0A%0A        if %22data%22 not in data:%0A            return None%0A%0A        # Check if we have more results on further pages%0A        if %22meta%22 in data:%0A            maxgoal = data%5B'meta'%5D.get('results', 100)%0A        else:%0A            maxgoal = 100%0A%0A        rescount = len(data%5B'data'%5D.get('emails', list()))%0A        print(%22RESCOUNT:%22 + str(rescount))%0A        print(%22MAXGOAL: %22 + str(maxgoal))%0A        while rescount %3C= maxgoal:%0A            for email in data%5B'data'%5D.get('emails', list()):%0A    
     
@@ -3582,32 +3582,36 @@
 und%0A            
+   
+ 
 e = SpiderFootEv
@@ -3669,32 +3669,36 @@
 nt)%0A            
+    
 self.notifyListe
@@ -3718,16 +3718,20 @@
         
+ 
+   
 if 'firs
@@ -3785,24 +3785,28 @@
             
+   
+ 
 if email%5B'fi
@@ -3876,16 +3876,20 @@
         
+    
 n = emai
@@ -3943,32 +3943,36 @@
                 
+  
+  
 e = SpiderFootEv
@@ -4020,16 +4020,20 @@
 %22 + n, %0A
+    
         
@@ -4098,32 +4098,36 @@
                 
+    
 self.notifyListe
@@ -4135,16 +4135,373 @@
 ers(e)%0A%0A
+            if rescount %3E= maxgoal:%0A                return None%0A%0A            data = self.query(eventData, rescount, 100)%0A            if data == None:%0A                return None%0A            if %22data%22 not in data:%0A                return None%0A%0A            rescount += len(data%5B'data'%5D.get('emails', list()))%0A            print(%22NEWRESCOUNT:%22 + str(rescount))%0A%0A
 %0A# End o

Support pagination in hunter.io.