use async_trait::async_trait;
use flume::Sender;
use reqwest::Url;
use tokio::sync::Mutex;
use crate::{
enums::{
dispatchers::{RequesterDispatcher, SubdomainExtractorDispatcher},
result::OptionalSubscanModuleResult,
},
interfaces::{
extractor::SubdomainExtractorInterface, module::SubscanModuleInterface,
requester::RequesterInterface,
},
types::{
core::SubscanModuleCoreComponents, query::SearchQueryParam,
result::status::SubscanModuleStatus::Finished,
},
};
pub struct GenericSearchEngineModule {
pub name: String,
pub url: Url,
pub param: SearchQueryParam,
pub components: SubscanModuleCoreComponents,
}
#[async_trait]
impl SubscanModuleInterface for GenericSearchEngineModule {
async fn name(&self) -> &str {
&self.name
}
async fn requester(&self) -> Option<&Mutex<RequesterDispatcher>> {
Some(&self.components.requester)
}
async fn extractor(&self) -> Option<&SubdomainExtractorDispatcher> {
Some(&self.components.extractor)
}
async fn run(&mut self, domain: &str, results: Sender<OptionalSubscanModuleResult>) {
let requester = &*self.components.requester.lock().await;
let extractor = &self.components.extractor;
let extra_params = [("num".to_string(), 100.to_string())];
let mut query = self.param.to_search_query(domain, "site:");
loop {
let url = query.as_url(self.url.clone(), &extra_params);
let content = requester.get_content(url).await;
match content {
Ok(content) => match extractor.extract(content, domain).await {
Ok(subdomains) => {
for subdomain in &subdomains {
results.send(self.item(subdomain).await).unwrap();
}
if !query.update_many(subdomains) {
results.send(self.status(Finished).await).unwrap();
break;
}
}
Err(err) => {
results.send(self.status(err.into()).await).unwrap();
break;
}
},
Err(err) => {
results.send(self.status(err.into()).await).unwrap();
break;
}
};
}
}
}