Advertisement
Guest User

Untitled

a guest
Feb 26th, 2016
274
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.78 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. import argparse
  4. import getpass
  5. import re
  6. import requests
  7.  
  8. from datetime import date, datetime
  9.  
  10.  
  11. # argparse stuff
  12. parser = argparse.ArgumentParser(description =
  13.         "A CSV fetcher for PSCU-based credit cards")
  14. parser.add_argument('csvfile', help='the csv output file',
  15.                     metavar='output.csv')
  16. parser.add_argument('--start', nargs=1, metavar='date',
  17.         help='csv start date in ISO 8601 (YYYY-MM-DD)',
  18.         type=lambda s: datetime.strptime(s, '%Y-%m-%d'))
  19. parser.add_argument('--end', nargs=1, metavar='date',
  20.         help='csv end date in ISO 8601 (YYYY-MM-DD)',
  21.         type=lambda s: datetime.strptime(s, '%Y-%m-%d'))
  22. args = parser.parse_args()
  23.  
  24. username = getpass.getpass("Username: ")
  25.  
  26. #TODO: add wicket:interface=:NUM tracking
  27. # Easiest way to get a session cookie is to GET the login page
  28. cookie_url = ("https://apstp.pscu.com/AP/APCardholder/pages/dsologin"
  29.               "?clientId=4356&siteFlag=true")
  30.  
  31. # We post the username to this URL
  32. # the jsessionid cookie is encoded in the URL for no good reason
  33. auth_user_url = ("https://apstp.pscu.com/AP/APCardholder/;jsessionid="
  34.                  "<SESSIONCOOKIE>?wicket:interface=:0:Components/"
  35.                  "CorePageContainer:Templates/CorePageTemplate:Components/"
  36.                  "CorePageComponentContainer:Components/DsoLogin:login:"
  37.                  "dsoLoginButton::IActivePageBehaviorListener:0:1"
  38.                  "&wicket:ignoreIfNotActive=true")
  39.  
  40. session_cookie_name = "JSESSIONID"
  41.  
  42. auth_user_payload = {"usernameDecorator:dataModel.userName": username,
  43.                      "dsoLoginButton": "1"}
  44.  
  45. # auth stage 2 is the security question
  46. auth2_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:1:"
  47.              "Components/CorePageContainer:Templates/CorePageTemplate:"
  48.              "Components/CorePageComponentContainer:Components/MfaQuestion:"
  49.              "validateQuestion:submitButton::IActivePageBehaviorListener:0:"
  50.              "&wicket:ignoreIfNotActive=true")
  51.  
  52. sec_question_pattern = "<label for=\"answer\" [:=\"\w\s]+>(.+)</label>"
  53. sec_question_payload = {"submitButton": "1"}
  54. sec_question_payload_key = "answerDecorator:answer"
  55.  
  56. # auth stage 3 is the password
  57. auth3_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:2:"
  58.              "Components/CorePageContainer:Templates/CorePageTemplate:"
  59.              "Components/CorePageComponentContainer:Components/Password:"
  60.              "validatePassword:submitButton::IActivePageBehaviorListener:0:"
  61.              "&wicket:ignoreIfNotActive=true")
  62.  
  63. password_payload = {"Wicket-FocusedElementId": "passwordSubmit",
  64.                     "group": "radio0", "submitButton": "1"}
  65.  
  66. password_payload_password_key = "passwordDecorator:password"
  67.  
  68. # transaction page / csv stuff
  69. transaction_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:"
  70.                    "3:Components/CorePageContainer:Templates/"
  71.                    "AccountSummaryTemplate:templateForm:Components/"
  72.                    "AccountSummaryDetails:listview:0:viewTxLink"
  73.                    "::ILinkListener::")
  74.  
  75. # the "API" sends POST requests for interface changes...
  76. # so here are a long list of URLs we have to POST to
  77. rangeselect_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface="
  78.                     ":4:Components/CorePageContainer:Templates/CorePage"
  79.                     "Template:Components/CorePageComponentContainer:Components"
  80.                     "/ViewTransactions:transactionDetailsForm:transactions"
  81.                     "Container:viewTransactionHistoryContainer:rangeSelection"
  82.                     "Container:selectPeriodDropDown::IBehaviorListener:0:")
  83.  
  84. rangeselect_payload = {("transactionsContainer:viewTransactionHistory"
  85.                          "Container:rangeSelectionContainer:"
  86.                          "selectPeriodDropDown"): "4"}
  87.  
  88. rangeselect_headers = {"Wicket-Ajax": "true",
  89.                         "Wicket-FocusedElementId": "period"}
  90.  
  91. date_format = "%m/%d/%Y"
  92.  
  93. dateselect_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:4"
  94.                   ":Components/CorePageContainer:Templates/CorePageTemplate:"
  95.                   "Components/CorePageComponentContainer:Components/View"
  96.                   "Transactions:transactionDetailsForm:transactionsContainer:"
  97.                   "viewTransactionHistoryContainer:rangeSelectionContainer:"
  98.                   "viewTransactionsHistorySelectionContainer:submitBtnLink::"
  99.                   "IActivePageBehaviorListener:0:-1"
  100.                   "&wicket:ignoreIfNotActive=true")
  101.  
  102. dateselect_payload = {("transactionsContainer:viewTransactionHistoryContainer:"
  103.                        "rangeSelectionContainer:selectPeriodDropDown"): "4",
  104.                       ("transactionsContainer:allTabContainer:Components/View"
  105.                        "AllTabTransactions:viewAllTabContainer:"
  106.                        "iwanttodropdown"): "0",
  107.                       ("transactionsContainer:viewTransactionHistoryContainer:"
  108.                        "rangeSelectionContainer:viewTransactionsHistory"
  109.                        "SelectionContainer:submitBtnLink"): "1"}
  110.                      
  111. dateselect_headers={"Wicket-Ajax": "true", "Wicket-FocusedElementId": "id460"}
  112.  
  113. dateselect_payload_sdate_key = ("transactionsContainer:viewTransactionHistory"
  114.                                 "Container:rangeSelectionContainer:"
  115.                                 "viewTransactionsHistorySelectionContainer:"
  116.                                 "fromDateDecorator:fromDate")
  117.  
  118. dateselect_payload_edate_key = ("transactionsContainer:viewTransactionHistory"
  119.                                 "Container:rangeSelectionContainer:"
  120.                                 "viewTransactionsHistorySelectionContainer:"
  121.                                 "toDateDecorator:toDate")
  122.  
  123. dropdown_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:4:"
  124.                 "Components/CorePageContainer:Templates/CorePageTemplate:"
  125.                 "Components/CorePageComponentContainer:Components/View"
  126.                 "Transactions:transactionDetailsForm:transactionsContainer:"
  127.                 "allTabContainer:Components/ViewAllTabTransactions:viewAllTab"
  128.                 "Container:iwanttodropdown::IBehaviorListener:0:-1")
  129.  
  130. dropdown_payload = {("transactionsContainer:allTabContainer:Components/View"
  131.                      "AllTabTransactions:viewAllTabContainer:iwanttodropdown"):
  132.                     "2"}
  133.  
  134. dropdown_headers={"Wicket-Ajax": "true", "Wicket-FocusedElementId": "id462"}
  135.  
  136. csv_url = ("https://apstp.pscu.com/AP/APCardholder/?wicket:interface=:4:"
  137.            "Components/CorePageContainer:Templates/CorePageTemplate:Components"
  138.            "/CorePageComponentContainer:Components/ViewTransactions:"
  139.            "transactionDetailsForm:transactionsContainer:allTabContainer:"
  140.            "Components/ViewAllTabTransactions:viewAllTabContainer:Components/"
  141.            "TransactionDownload::IActivePageBehaviorListener:2:-1"
  142.            "&wicket:ignoreIfNotActive=true")
  143.  
  144.  
  145. # validate user date input
  146. # TODO make this prettier
  147. def get_date(name):
  148.     while True:
  149.         try:
  150.             d = input("Input {} date in YYYY-MM-DD format: ".format(name))
  151.             d = datetime.strptime(d, "%Y-%m-%d")
  152.             return d
  153.         except:
  154.             pass
  155.  
  156.  
  157. # requests session
  158. # have to use this because pscu API is shit and sends us lots of cookies
  159. s = requests.Session()
  160.  
  161. # get session cookies
  162. page1 = s.get(cookie_url)
  163. session_cookie = page1.cookies[session_cookie_name]
  164.  
  165. # add session cookie to auth_user_url (wtf?)
  166. auth_user_url = auth_user_url.replace("<SESSIONCOOKIE>", session_cookie)
  167.  
  168. # auth the user
  169. auth1 = s.post(auth_user_url, data=auth_user_payload)
  170.  
  171. print("Getting security question...")
  172.  
  173. # find security questions
  174. # TODO: use beautifulsoup for parsing instead of a regex?
  175. sec_question = re.findall(sec_question_pattern, auth1.text)[0]
  176. sec_question_answer = input(sec_question + " ")
  177. sec_question_payload[sec_question_payload_key] = sec_question_answer
  178.  
  179. # send security question
  180. # TODO: handle failure case
  181. print("Submitting security question")
  182. auth2 = s.post(auth2_url, data=sec_question_payload)
  183.  
  184. # get password from user
  185. password = getpass.getpass()
  186. print("Logging in...")
  187.  
  188. # auth the password
  189. password_payload[password_payload_password_key] = password
  190. auth3 = s.post(auth3_url, data=password_payload)
  191.  
  192. # get a date range
  193. start_date = args.start[0] if args.start else get_date("start")
  194. start_date = start_date.strftime(date_format)
  195. end_date = args.end[0] if args.end else get_date("end")
  196. end_date = end_date.strftime(date_format)
  197.  
  198. dateselect_payload[dateselect_payload_sdate_key] = start_date
  199. dateselect_payload[dateselect_payload_edate_key] = end_date
  200.  
  201. # Getting the CSV is done in 4 stages because the site works by
  202. # tracking the active HTML elements serverside, so we have to
  203. # update them individually with post requests. In order, they are:
  204. # 1. Get the transaction page to activate its DOM elements
  205. # 2. Send a POST to switch the range select to "date" mode
  206. # 3. Send a POST to switch the date range to the user's selection
  207. # 4. Send a POST to active the dropdown to download the CSV
  208. # Then we send a GET request for the CSV, and the server uses our
  209. # current DOM state to send the correct CSV
  210. # fucking insanity
  211.  
  212. print("Downloading csv...")
  213.  
  214. # need to get transaction page to initialize form so POST can update it
  215. t_page = s.get(transaction_url)
  216.  
  217. # post the rangeselect, then dateselect, then dropdown
  218. s.post(rangeselect_url, data=rangeselect_payload, headers=rangeselect_headers)
  219. s.post(dateselect_url, data=dateselect_payload, headers=dateselect_headers)
  220. s.post(dropdown_url, data=dropdown_payload, headers=dropdown_headers)
  221.  
  222. # grab the csv
  223. csv = s.get(csv_url)
  224.  
  225. with open(args.csvfile, 'w') as f:
  226.     f.write(csv.text)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement