76 public class GoogleDorking : IGoogleDorking
79 internal IRESTClient SearchRESTClient {
get;
private set; }
80 public string ApiKey {
get;
private set; }
81 public string EngineId {
get;
private set; }
82 internal List<string> QueryParts {
get;
private set; }
83 internal int ResultsPerPage {
get;
private set; } = 10;
84 internal string DateRestrict {
get;
private set; }
85 internal string SortBy {
get;
private set; }
86 internal bool IsExactPhrase {
get;
private set; }
88 private string ProjectName {
get;
set; }
89 private string ProjectId {
get;
set; }
91 public GoogleDorking()
94 SearchRESTClient =
GPAL.
RESTClient.WithAPIBase(Config.SearchApiBase).ToGPALObject();
95 QueryParts =
new List<string>();
96 ApiKey = Config.ApiKey;
97 EngineId = Config.EngineId;
100 public IAllowDorkingEngineSelection WithApiKey(
string apiKey)
102 if (
true ==
string.IsNullOrEmpty(apiKey) ||
false == IsValidApiKeyFormat(apiKey))
104 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Invalid or empty API key format. Expected format: AIzaSy followed by ~39 characters.",
null, GPALObjectType.None);
109 Config.ApiKey = apiKey;
114 public IAllowGenerateAPIKey WithCredentials(
ICredentials credentials)
116 Credentials = credentials;
120 public IAllowGenerateAPIKey WithProjectName(
string projectName)
122 ProjectName = projectName;
126 public IAllowGenerateAPIKey WithProjectId(
string projectId)
128 ProjectId = projectId;
132 public IAllowDorkingApiKey GenerateApiKey(out
string apiKey)
134 apiKey =
string.Empty;
135 bool existingPublishToConsole =
GPAL.PublishToConsole;
137 if (
null == Credentials)
139 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"No credentials provided for API key generation.",
null, GPALObjectType.None);
143 var creds = (Credentials)Credentials;
144 if (CredentialServiceType.Google != creds.ServiceType)
146 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Credentials must be for Google service.",
null, GPALObjectType.None);
151 if (
false == existingPublishToConsole)
154 GPAL.
PublishSimpleEvent(GPALEventType.INFO,
"Ensure Cloud Resource Manager API is enabled: https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com",
null, GPALObjectType.None);
156 if (
false == existingPublishToConsole)
159 string googleProjectId = googleCloud.CreateProject(ProjectName, ProjectId);
161 if (
true ==
string.IsNullOrEmpty(googleProjectId))
163 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"PROJECT NOT CREATED:Project Name [{ProjectName ?? googleProjectId}]\nProjectId [{ProjectId ?? googleProjectId}] Google ProjectId [{googleProjectId}]",
this, GPALObjectType.GoogleDorking);
167 bool success = googleCloud.EnableApi(googleProjectId, GoogleApi.ServiceUsage) &&
168 googleCloud.EnableApi(googleProjectId, GoogleApi.ApiKeys) &&
169 googleCloud.EnableApi(googleProjectId, GoogleApi.CustomSearch);
175 apiKey = googleCloud.GenerateApiKey(googleProjectId);
177 if (
true !=
string.IsNullOrEmpty(apiKey))
179 if (
false == existingPublishToConsole)
183 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Write this information down:\nAPI KEY: [{apiKey}]\nProject Name [{ProjectName ?? googleProjectId}]\nProjectId [{ProjectId ?? googleProjectId}] Google ProjectId [{googleProjectId}]",
this, GPALObjectType.GoogleDorking);
184 if (
false == existingPublishToConsole)
193 public IAllowDorkingQueryBuilding WithDateRestriction(DateRestriction restriction,
int? customValue =
null)
195 DateRestrict = restriction
switch
197 DateRestriction.PastDay =>
"d1",
198 DateRestriction.PastWeek =>
"w1",
199 DateRestriction.PastMonth =>
"m1",
200 DateRestriction.PastYear =>
"y1",
201 DateRestriction.Custom when customValue.HasValue => $
"m{customValue.Value}",
204 if (
true == DateRestriction.Custom.Equals(restriction) &&
false == customValue.HasValue)
206 GPAL.
PublishSimpleEvent(GPALEventType.WARNING,
"Custom date restriction requires a value",
null, GPALObjectType.None);
211 public IAllowDorkingQueryBuilding WithSortBy(SortOption sortBy)
213 SortBy = sortBy
switch
215 SortOption.Date =>
"date",
216 SortOption.Relevance =>
"",
222 public IAllowDorkingQueryExecution SearchFor(
string searchTerm)
224 if (
false ==
string.IsNullOrEmpty(searchTerm))
226 QueryParts.Insert(0,
true == IsExactPhrase ? $
"\"{Uri.EscapeDataString(searchTerm)}\"" : Uri.EscapeDataString(searchTerm));
232 IsExactPhrase =
false;
236 public IAllowDorkingQueryBuilding GetResults(out IGPALGrid<string> results)
238 results =
GPAL.GridForType<
string>();
240 if (
true ==
string.IsNullOrEmpty(ApiKey) ||
true ==
string.IsNullOrEmpty(EngineId) ||
false == QueryParts.Any())
242 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Missing required setup (API key, engine ID, or query)",
null, GPALObjectType.None);
249 var queryString =
string.Join(
" ", QueryParts);
250 var parameters =
new Dictionary<string, string>
252 {
"q", queryString },
253 {
"cx", Uri.EscapeDataString(EngineId) },
254 {
"fields", Uri.EscapeDataString(Config.QueryFields) },
255 {
"key", Uri.EscapeDataString(ApiKey) }
258 if (0 < ResultsPerPage)
260 parameters.Add(
"num", ResultsPerPage.ToString());
262 if (
false ==
string.IsNullOrEmpty(DateRestrict))
264 parameters.Add(
"dateRestrict", DateRestrict);
266 if (
false ==
string.IsNullOrEmpty(SortBy))
268 parameters.Add(
"sort", SortBy);
271 var endpoint = $
"{Config.SearchEndpoint}?{string.Join("&
", parameters.Select(p => $"{p.Key}={p.Value}
"))}";
273 var response = SearchRESTClient
274 .WithEndpoint(endpoint)
277 if (
null != response?.Items)
279 foreach (var item
in response.Items)
281 results.AddRow(
new List<string> { item.Title, item.Link, item.Snippet });
286 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Failed to retrieve search results",
null, GPALObjectType.None);
291 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to retrieve search results",
null, GPALObjectType.None, ex);
298 public IGoogleDorking ToGPALObject()
303 public static bool IsValidApiKeyFormat(
string apiKey)
305 return false ==
string.IsNullOrEmpty(apiKey) &&
true == apiKey.StartsWith(
"AIzaSy") && 30 <= apiKey.Length && 50 >= apiKey.Length;
308 private void ResetQuery()
313 IsExactPhrase =
false;
316 public IAllowDorkingQueryBuilding WithEngineId(
string engineId)
318 if (
true ==
string.IsNullOrEmpty(engineId))
324 Config.EngineId = engineId;
329 public IAllowDorkingQueryBuilding WithExcludeTerm(
string term)
331 if (
false ==
string.IsNullOrEmpty(term))
333 QueryParts.Add($
"-{Uri.EscapeDataString(term)}");
338 public IAllowDorkingQueryBuilding WithSite(
string site)
340 if (
false ==
string.IsNullOrEmpty(site))
342 QueryParts.Add($
"site:{Uri.EscapeDataString(site)}");
347 public IAllowDorkingQueryBuilding WithFileType(FileType fileType)
349 string fileTypeStr = fileType
switch
351 FileType.Pdf =>
"pdf",
352 FileType.Doc =>
"doc",
353 FileType.Docx =>
"docx",
354 FileType.Txt =>
"txt",
355 FileType.Xls =>
"xls",
356 FileType.Xlsx =>
"xlsx",
359 if (
false ==
string.IsNullOrEmpty(fileTypeStr))
361 QueryParts.Add($
"filetype:{fileTypeStr}");
366 public IAllowDorkingQueryBuilding WithInTitle(
string term)
368 if (
false ==
string.IsNullOrEmpty(term))
370 QueryParts.Add($
"intitle:{Uri.EscapeDataString(term)}");
375 public IAllowDorkingQueryBuilding WithInUrl(
string term)
377 if (
false ==
string.IsNullOrEmpty(term))
379 QueryParts.Add($
"inurl:{Uri.EscapeDataString(term)}");
384 public IAllowDorkingQueryBuilding WithResultsPerPageUpTo10(
int num)
386 if (1 <= num && 10 >= num)
388 ResultsPerPage = num;
392 GPAL.
PublishSimpleEvent(GPALEventType.WARNING,
"Results per page must be between 1 and 10.",
null, GPALObjectType.None);
397 public IAllowDorkingQueryExecution SearchForExactPhrase(
string searchTerm)
399 IsExactPhrase =
true;
400 return SearchFor(searchTerm);