Advertisement
astrit

Untitled

Feb 10th, 2016
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.53 KB | None | 0 0
  1. public final class GenieSearcher {
  2.  
  3. private String username;
  4. private String password;
  5. private String useragent;
  6.  
  7. private String loginFormUrl;
  8. private String loginUrl;
  9.  
  10. private String changeAreaSettingsUrl;
  11. private String changeZipSettingsUrl;
  12. private String changeSicSettingsUrl;
  13. private String dataSearchUrl;
  14. private String sessionId;
  15.  
  16. private String[] lastSentCounties = new String[0];
  17. private String[] lastSentStates = new String[0];
  18. private String[] lastSentZips = new String[0];
  19. private String[] lastSentSics = new String[0];
  20.  
  21. private CloseableHttpClient client = null;
  22.  
  23. private final List<Callback<SearchEntry>> parseCallbacks = new ArrayList<Callback<SearchEntry>>();
  24.  
  25. private boolean running = true;
  26. private int cachedRecordCount = 0;
  27.  
  28. /**
  29. * @param username
  30. * @param password
  31. * @param useragent
  32. */
  33. public GenieSearcher(String username, String password) {
  34. this.username = username;
  35. this.password = password;
  36.  
  37. try {
  38. reset();
  39. parseConfig();
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. }
  43. }
  44.  
  45. /**
  46. * Parses all external configuration data related to this bot.
  47. *
  48. * @throws IOException
  49. */
  50. private void parseConfig() throws IOException {
  51. Map<String, String> kv = ConfigurationParser.parse(new File("./data/bot.cfg"));
  52.  
  53. this.loginFormUrl = kv.get("login_form_url");
  54. this.loginUrl = kv.get("login_url");
  55.  
  56. this.changeAreaSettingsUrl = kv.get("change_area_settings_url");
  57. this.changeZipSettingsUrl = kv.get("change_zip_settings_url");
  58. this.changeSicSettingsUrl = kv.get("change_sic_settings_url");
  59.  
  60. this.dataSearchUrl = kv.get("data_search_url");
  61. this.useragent = kv.get("user_agent");
  62. }
  63.  
  64. /**
  65. * Resets all state currently held by this bot.
  66. *
  67. * @throws Exception
  68. */
  69. private void reset() throws Exception {
  70. if (client != null) {
  71. client.close();
  72. }
  73.  
  74. SSLContextBuilder builder = new SSLContextBuilder();
  75. builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
  76. SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
  77.  
  78. client = HttpClients.custom().
  79. setUserAgent(useragent).
  80. setSSLSocketFactory(sslsf).build();
  81. }
  82.  
  83. /**
  84. * Attempts to login using the user name and password provided during initialization, and
  85. * then proceeds to attempt to pass the static authentication page redirected to by the web server.
  86. *
  87. * @throws IOException
  88. */
  89. public void login() throws IOException {
  90. HttpGet get = new HttpGet(loginFormUrl);
  91. appendFakeHeaders(get);
  92. CloseableHttpResponse loginFormResponse = client.execute(get);
  93.  
  94. Document document = Jsoup.parse(HttpClientUtil.textual(loginFormResponse.getEntity()));
  95.  
  96. String token = document.select("input[name=__RequestVerificationToken]").first().attr("value");
  97. String signinUrl = document.getElementById("OriginalSigninUrl").attr("value");
  98. String returnUrl = document.getElementById("ReturnUrl").attr("value");
  99. String wa = document.getElementById("Wa").attr("value");
  100. String wct = document.getElementById("Wct").attr("value");
  101. String wctx = document.getElementById("Wctx").attr("value");
  102. String wtrealm = document.getElementById("Wtrealm").attr("value");
  103.  
  104. HttpPost post = new HttpPost(loginUrl);
  105. List<NameValuePair> params = new ArrayList<NameValuePair>();
  106. params.add(new BasicNameValuePair("UserName", username));
  107. params.add(new BasicNameValuePair("Password", password));
  108. params.add(new BasicNameValuePair("__RequestVerificationToken", token));
  109. params.add(new BasicNameValuePair("EnableSSO", "False"));
  110. params.add(new BasicNameValuePair("OriginalSigninUrl", signinUrl));
  111. params.add(new BasicNameValuePair("RememberHRDSelection", "True"));
  112. params.add(new BasicNameValuePair("ReturnUrl", returnUrl));
  113. params.add(new BasicNameValuePair("Wa", wa));
  114. params.add(new BasicNameValuePair("Wct", wct));
  115. params.add(new BasicNameValuePair("Wctx", wctx));
  116. params.add(new BasicNameValuePair("Wtrealm", wtrealm));
  117. params.add(new BasicNameValuePair("formSignIn", ""));
  118. post.setEntity(new UrlEncodedFormEntity(params));
  119.  
  120. appendFakeHeaders(post);
  121. CloseableHttpResponse loginResponse = client.execute(post);
  122. if (loginResponse.getStatusLine().getStatusCode() != 302) {
  123. throw new IOException("Login page failed to redirect!");
  124. }
  125.  
  126. document = Jsoup.parse(HttpClientUtil.textual(loginResponse.getEntity()));
  127.  
  128. // After successfully logging in, any attempt to access another page will result in a static
  129. // authentication page one time; we'll go through that here.
  130. String localRedirectUrl = loginResponse.getHeaders("Location")[0].getValue();
  131. handleAuthPage(localRedirectUrl);
  132.  
  133. retrieveSessionId();
  134. }
  135.  
  136. /**
  137. * Navigates through the authentication page at the provided (local) URL.
  138. *
  139. * @param localUrl
  140. * @throws IOException
  141. */
  142. private void handleAuthPage(String localUrl) throws IOException {
  143. HttpGet get = new HttpGet("https://auth.salesgenie.com" + localUrl);
  144.  
  145. appendFakeHeaders(get);
  146. CloseableHttpResponse authResponse = client.execute(get);
  147. Document document = Jsoup.parse(HttpClientUtil.textual(authResponse));
  148.  
  149. String wa = document.select("input[name=wa]").attr("value");
  150. String wresult = document.select("input[name=wresult]").attr("value");
  151. String wctx = document.select("input[name=wctx]").attr("value");
  152.  
  153. HttpPost post = new HttpPost("https://app.salesgenie.com/Home/Home");
  154. List<NameValuePair> params = new ArrayList<NameValuePair>();
  155. params.add(new BasicNameValuePair("wa", wa));
  156. params.add(new BasicNameValuePair("wresult", wresult));
  157. params.add(new BasicNameValuePair("wctx", wctx));
  158. post.setEntity(new UrlEncodedFormEntity(params));
  159.  
  160. appendFakeHeaders(post);
  161. client.execute(post);
  162. }
  163.  
  164. /**
  165. * Retrieves the currently in use session id from the build list page.
  166. *
  167. * @throws IOException
  168. */
  169. public void retrieveSessionId() throws IOException {
  170. // https://app.salesgenie.com/UsBusiness/ResultView/
  171. String url = "https://app.salesgenie.com/UsBusiness/ResultView/"; //https://app.salesgenie.com/UsBusiness/Search/Quick?
  172. HttpPost post = new HttpPost(url);
  173. List<NameValuePair> params = new ArrayList<NameValuePair>();
  174. params.add(new BasicNameValuePair("businessName", ""));
  175. params.add(new BasicNameValuePair("executiveFirstName", ""));
  176. params.add(new BasicNameValuePair("rangeStreetNumber", ""));
  177. params.add(new BasicNameValuePair("rangeStreetCity", ""));
  178. params.add(new BasicNameValuePair("rangeStreetZip", ""));
  179. params.add(new BasicNameValuePair("phone", ""));
  180. params.add(new BasicNameValuePair("executiveLastName", ""));
  181. params.add(new BasicNameValuePair("rangeStreetName", ""));
  182. params.add(new BasicNameValuePair("rangeStreetState", "VA"));
  183. post.setEntity(new UrlEncodedFormEntity(params));
  184.  
  185. appendFakeHeaders(post);
  186. CloseableHttpResponse response = client.execute(post);
  187. Document document = Jsoup.parse(HttpClientUtil.textual(response));
  188. System.out.println(document.text());
  189. String textual = document.text();
  190.  
  191. JSONObject obj = new JSONObject(textual);
  192. sessionId = obj.getJSONObject("data").getString("RequestKey");
  193.  
  194. this.sendAreaSettings(new String[0], new String[0]);
  195. }
  196.  
  197. /**
  198. * Updates the area settings the web server will use to conduct data searches.
  199. *
  200. * @param counties
  201. * @param states
  202. * @throws IOException
  203. */
  204. public void sendAreaSettings(String[] counties, String[] states) throws IOException {
  205. this.lastSentCounties = counties;
  206. this.lastSentStates = states;
  207.  
  208. StringBuilder countyParamBuilder = new StringBuilder();
  209. if (counties.length > 0) {
  210. for (String s : counties) {
  211. countyParamBuilder.append(s).append(",");
  212. }
  213. countyParamBuilder.setLength(countyParamBuilder.length() - 1);
  214. }
  215.  
  216. StringBuilder stateParamBuilder = new StringBuilder();
  217. if (states.length > 0) {
  218. for (String s : states) {
  219. stateParamBuilder.append(s).append(",");
  220. }
  221. stateParamBuilder.setLength(stateParamBuilder.length() - 1);
  222. }
  223.  
  224. HttpPost post = new HttpPost(changeAreaSettingsUrl + sessionId);
  225. List<NameValuePair> params = new ArrayList<NameValuePair>();
  226. params.add(new BasicNameValuePair("cities", ""));
  227. params.add(new BasicNameValuePair("counties", countyParamBuilder.toString()));
  228. params.add(new BasicNameValuePair("states", stateParamBuilder.toString()));
  229. params.add(new BasicNameValuePair("microAreas", ""));
  230. params.add(new BasicNameValuePair("metroAreas", ""));
  231. params.add(new BasicNameValuePair("commit", "true"));
  232. post.setEntity(new UrlEncodedFormEntity(params));
  233.  
  234. appendFakeHeaders(post);
  235. CloseableHttpResponse response = client.execute(post);
  236. String textual = HttpClientUtil.textual(response);
  237. JSONObject container = new JSONObject(textual);
  238. cachedRecordCount = container.getJSONObject("data").getInt("MatchCount");
  239.  
  240. EntityUtils.consume(response.getEntity());
  241. response.close();
  242. }
  243.  
  244. /**
  245. * Updates the ZIP code settings the web server will use to conduct data searches.
  246. *
  247. * @param zips
  248. * @throws IOException
  249. */
  250. public void sendZipSettings(String[] zips) throws IOException {
  251. this.lastSentZips = zips;
  252.  
  253. StringBuilder zipsParamBuilder = new StringBuilder();
  254. if (zips.length > 0) {
  255. for (String s : zips) {
  256. zipsParamBuilder.append(s).append(" ");
  257. }
  258. zipsParamBuilder.setLength(zipsParamBuilder.length() - 1);
  259. }
  260.  
  261. HttpPost post = new HttpPost(changeZipSettingsUrl + sessionId);
  262. List<NameValuePair> params = new ArrayList<NameValuePair>();
  263. params.add(new BasicNameValuePair("pastedZipCodes", zipsParamBuilder.toString()));
  264. params.add(new BasicNameValuePair("zipLookup", ""));
  265. params.add(new BasicNameValuePair("carrierRouteGrid", ""));
  266. params.add(new BasicNameValuePair("commit", "true"));
  267. post.setEntity(new UrlEncodedFormEntity(params));
  268.  
  269. appendFakeHeaders(post);
  270. CloseableHttpResponse response = client.execute(post);
  271. String textual = HttpClientUtil.textual(response);
  272. JSONObject container = new JSONObject(textual);
  273. cachedRecordCount = container.getJSONObject("data").getInt("MatchCount");
  274.  
  275. EntityUtils.consume(response.getEntity());
  276. response.close();
  277. }
  278.  
  279. /**
  280. * Updates the SIC settings the web server will use to conduct data searches.
  281. *
  282. * @param sicLookups
  283. * @throws IOException
  284. */
  285. public void sendSicSettings(String[] sicLookups) throws IOException {
  286. this.lastSentSics = sicLookups;
  287.  
  288. StringBuilder sicLookupParamBuilder = new StringBuilder();
  289. if (sicLookups.length > 0) {
  290. for (String s : sicLookups) {
  291. sicLookupParamBuilder.append(s).append(" ");
  292. }
  293. sicLookupParamBuilder.setLength(sicLookupParamBuilder.length() - 1);
  294. }
  295.  
  296. HttpPost post = new HttpPost(changeSicSettingsUrl + sessionId);
  297. List<NameValuePair> params = new ArrayList<NameValuePair>();
  298. params.add(new BasicNameValuePair("pastedSicCodes", ""));
  299. params.add(new BasicNameValuePair("pastedNAICSCodes", ""));
  300. params.add(new BasicNameValuePair("sicLookup", sicLookupParamBuilder.toString()));
  301. params.add(new BasicNameValuePair("naicsLookup", ""));
  302. params.add(new BasicNameValuePair("numericalSic", ""));
  303. params.add(new BasicNameValuePair("pastedSic", ""));
  304. params.add(new BasicNameValuePair("primarySicOnly", "false"));
  305. params.add(new BasicNameValuePair("primaryNumericalSicOnly", "false"));
  306. params.add(new BasicNameValuePair("numericalNaics", ""));
  307. params.add(new BasicNameValuePair("pastedNaics", ""));
  308. params.add(new BasicNameValuePair("primaryNaicsOnly", "false"));
  309. params.add(new BasicNameValuePair("primaryNumericalNaicsOnly", "false"));
  310. params.add(new BasicNameValuePair("commit", "true"));
  311. post.setEntity(new UrlEncodedFormEntity(params));
  312.  
  313. appendFakeHeaders(post);
  314. CloseableHttpResponse response = client.execute(post);
  315. String textual = HttpClientUtil.textual(response);
  316. JSONObject container = new JSONObject(textual);
  317. cachedRecordCount = container.getJSONObject("data").getInt("MatchCount");
  318.  
  319. EntityUtils.consume(response.getEntity());
  320. response.close();
  321. }
  322.  
  323. /**
  324. * Attempts to query a list of data entries from the web server.
  325. *
  326. * @param page
  327. * @param querySize
  328. * @return The JSON result from the web server
  329. * @throws IOException
  330. */
  331. private JSONObject queryListing(int page, int querySize) throws IOException {
  332. HttpPost post = new HttpPost(dataSearchUrl + sessionId);
  333. List<NameValuePair> params = new ArrayList<NameValuePair>();
  334. params.add(new BasicNameValuePair("iDisplayStart", Integer.toString(page * querySize)));
  335. params.add(new BasicNameValuePair("sEcho", Integer.toString(page + 1)));
  336. post.setEntity(new UrlEncodedFormEntity(params));
  337. appendFakeHeaders(post);
  338.  
  339. CloseableHttpResponse response = client.execute(post);
  340. String textual = HttpClientUtil.textual(response);
  341. if (!textual.startsWith("{")) {
  342. throw new IOException("Invalid response received (not JSON)");
  343. }
  344.  
  345. JSONObject container = new JSONObject(textual);
  346. if (!container.has("aaData")) {
  347. throw new IOException("Invalid response received (no aaData object in JSON response)");
  348. }
  349.  
  350. return container;
  351. }
  352.  
  353. /**
  354. * Will attempt to query for all data entries, with exception handling to avoid crashes.
  355. *
  356. * @param querySize
  357. * The size of each query chunk
  358. */
  359. public void safeQueryAll(int querySize) throws IOException {
  360. this.running = true;
  361.  
  362. // store these here, because in the case of an exception
  363. // all state will be reset, including last sent information (while retrieving new session id)
  364. String[] cachedCounties = this.lastSentCounties;
  365. String[] cachedStates = this.lastSentStates;
  366. String[] cachedZips = this.lastSentZips;
  367. String[] cachedSics = this.lastSentSics;
  368.  
  369. int queryCount = cachedRecordCount / querySize;
  370.  
  371. for (int i = 0; i < queryCount; i++) {
  372. try {
  373. JSONObject container = queryListing(i, querySize);
  374. JSONArray rows = container.getJSONArray("aaData");
  375.  
  376. for (int j = 0; j < rows.length(); j++) {
  377. JSONObject current = rows.getJSONObject(j);
  378.  
  379. if (!current.has("state")) {
  380. throw new IOException("Invalid response received (row missing state)");
  381. } else if (!current.has("zIP")) {
  382. throw new IOException("Invalid response received (row zip)");
  383. } else if (!current.has("cityV2")) {
  384. throw new IOException("Invalid response received (row missing city)");
  385. } else if (!current.has("streetAddress")) {
  386. throw new IOException("Invalid response received (row missing street address)");
  387. } else if (!current.has("businessName")) {
  388. throw new IOException("Invalid response received (row missing business name)");
  389. } else if (!current.has("executiveName")) {
  390. throw new IOException("Invalid response received (row missing executive name)");
  391. } else if (!current.has("phone")) {
  392. throw new IOException("Invalid response received (row missing phone)");
  393. }
  394.  
  395. Document stateDoc = Jsoup.parse(current.getString("state"));
  396. Document zipDoc = Jsoup.parse(current.getString("zIP"));
  397. Document cityDoc = Jsoup.parse(current.getString("cityV2"));
  398. Document streetDoc = Jsoup.parse(current.getString("streetAddress"));
  399. Document businessDoc = Jsoup.parse(current.getString("businessName"));
  400. Document executiveDoc = Jsoup.parse(current.getString("executiveName"));
  401. Document phoneDoc = Jsoup.parse(current.getString("phone"));
  402.  
  403. String state = stateDoc.text();
  404. String zip = zipDoc.text();
  405. String city = cityDoc.text();
  406. String street = streetDoc.text();
  407. String business = businessDoc.text();
  408. String fullName = executiveDoc.text().trim();
  409. String phone = phoneDoc.text();
  410.  
  411. SearchEntry e = new SearchEntry(state, zip, city, street, business, fullName, phone);
  412. for (Callback<SearchEntry> callback : parseCallbacks) {
  413. callback.callback(e);
  414. }
  415. }
  416. } catch (IOException e) {
  417. System.out.println("Error querying data -> attempting to relog (" + e.getMessage() + ")");
  418.  
  419. while (true) {
  420. try {
  421. this.reset();
  422. this.login();
  423.  
  424. this.sendAreaSettings(cachedCounties, cachedStates);
  425. this.sendZipSettings(cachedZips);
  426. this.sendSicSettings(cachedSics);
  427.  
  428. break;
  429. } catch (Exception e2) {
  430. e2.printStackTrace();
  431. }
  432. }
  433. }
  434.  
  435. if (!this.running) {
  436. break;
  437. }
  438. }
  439.  
  440. this.running = false;
  441. }
  442.  
  443. /**
  444. * Registers a Callback that'll be fired off when a new data entry is parsed from the web server.
  445. *
  446. * @param callback
  447. */
  448. public void registerParseCallback(Callback<SearchEntry> callback) {
  449. this.parseCallbacks.add(callback);
  450. }
  451.  
  452. /**
  453. * @return If a data search is currently running or not
  454. */
  455. public boolean isRunningSearch() {
  456. return running;
  457. }
  458.  
  459. /**
  460. * Stops the currently running data search after it's finished with the
  461. * query it's currently on.
  462. */
  463. public void stopRunning() {
  464. this.running = false;
  465. }
  466.  
  467. /**
  468. * @return The amount of total data entry records in the last submitted search
  469. */
  470. public int getCachedRecordCount() {
  471. return this.cachedRecordCount;
  472. }
  473.  
  474. /**
  475. * Appends fake headers to the provided HttpRequestBase to make it appear like a legitimate request
  476. * made from a web browser.
  477. *
  478. * @param base
  479. */
  480. private static void appendFakeHeaders(HttpRequestBase base) {
  481. base.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
  482. base.addHeader("Accept-Encoding", "gzip, deflate");
  483. base.addHeader("Connection", "keep-alive");
  484. base.addHeader("Host", "salesgenie.com");
  485. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement