Browse Source

Initial code commit

First draft taking code from Walter Huf and adding pieces of the
JIRACookieAuth (early drafts, needs revision) to handle relogins.
Original repo was
https://bitbucket.org/geekytwink/dudesnude_mail.git
for reference to Walter's code.
master
Nate Bohman 6 years ago
parent
commit
51fc6658ac
5 changed files with 332 additions and 0 deletions
  1. +15
    -0
      .idea/dudesnude_client.iml
  2. +4
    -0
      .idea/misc.xml
  3. +8
    -0
      .idea/modules.xml
  4. +121
    -0
      .idea/workspace.xml
  5. +184
    -0
      client.py

+ 15
- 0
.idea/dudesnude_client.iml View File

<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="PackageRequirementsSettings">
<option name="requirementsPath" value="" />
</component>
<component name="TestRunnerService">
<option name="projectConfiguration" value="Twisted Trial" />
<option name="PROJECT_TEST_RUNNER" value="Twisted Trial" />
</component>
</module>

+ 4
- 0
.idea/misc.xml View File

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7" project-jdk-type="Python SDK" />
</project>

+ 8
- 0
.idea/modules.xml View File

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/dudesnude_client.iml" filepath="$PROJECT_DIR$/.idea/dudesnude_client.iml" />
</modules>
</component>
</project>

+ 121
- 0
.idea/workspace.xml View File

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="b72e990d-b17a-470e-b0c4-327d1e6d5129" name="Default" comment="" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf>
<file leaf-file-name="client.py" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/client.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
</leaf>
</component>
<component name="ProjectFrameBounds" extendedState="1">
<option name="x" value="140" />
<option name="y" value="20" />
<option name="width" value="1400" />
<option name="height" value="990" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="dudesnude_client" type="b2602c69:ProjectViewProjectNode" />
<item name="dudesnude_client" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="settings.editor.selected.configurable" value="preferences.startup.tasks" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="b72e990d-b17a-470e-b0c4-327d1e6d5129" name="Default" comment="" />
<created>1530625076390</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1530625076390</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="140" y="20" width="1400" height="990" extended-state="0" />
<editor active="true" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.25" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Version Control" show_stripe_button="false" />
<window_info anchor="bottom" id="Python Console" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Terminal" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info id="Favorites" side_tool="true" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Message" order="0" />
</layout>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/client.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</component>
<component name="masterDetails">
<states>
<state key="ScopeChooserConfigurable.UI">
<settings>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

+ 184
- 0
client.py View File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Standard library imports
import argparse
import errno
import logging
import os
import re
import threading

# Related third party imports (If you used pip/apt/yum to install)
import arrow
from bs4 import BeautifulSoup as bs
from expand_path import expand_path
import requests
from requests.auth import AuthBase
from shm_dict import SHMDict

__author__ = "Nate Bohman"
__license__ = "GPL-3"
__version__ = "1.1.0"
__maintainer__ = "Nate Bohman"
__email__ = "natrinicle@gmail.com"

logger = logging.getLogger(__name__)

try:
unichr
except NameError:
unichr = chr

try:
unicode
except NameError:
unicode = str


DEFAULT_CFGDIR_BASE = expand_path("~/.config/dudesnude")
LOGIN_URL = {"default": "https://dudesnude.com/login.php",
"guest": "http://dudesnude.com/warning.php"}
LOGIN_INPUT_RGX = re.compile(r'<input.*name="(?P<name>[^"]+)".*value="(?P<value>[^"]+)"', re.I)


class DudesNudeCookieAuth(AuthBase):
def __init__(self, email, password="", cookie_dir=DEFAULT_CFGDIR_BASE):
self.cookie_dict = None
self._cookie_dir = ""
self.cookie_dir = cookie_dir

self._email = ""
self.email = email
self.password = password

self.session = requests.Session()

@property
def email(self):
return self._email

@email.setter
def email(self, value):
if self._email != value:
self._email = value
self.cookie_dict = SHMDict(name=self.cookie_file,
size=8192, is_file=True)

@property
def cookie_dir(self):
return self._cookie_dir

@cookie_dir.setter
def cookie_dir(self, value):
value = expand_path(value)

self._cookie_dir = value

try:
os.makedirs(value, mode=0o750)
except OSError as e:
if e.errno != errno.EEXIST:
raise


@property
def cookie_file(self):
return "{}/{}_cookies".format(self.cookie_dir,
self.email)

@property
def login_form_fields(self):
r = self.session.get(LOGIN_URL.get("default"))
self.cookie_dict['cookies'] = self.session.cookies

login_form_fields = {}
if LOGIN_INPUT_RGX.search(r.text):
for login_input in LOGIN_INPUT_RGX.finditer(r.text):
login_input = login_input.groupdict()
login_form_fields[login_input.get("name")] = login_input.get("value")
return login_form_fields

@property
def login_data(self):
if self.email == "guest":
return {'go': '1', 'iagree': '1', 'token': '', 'win_x': '1080', 'win_y': '720'}
else:
login_form_fields = self.login_form_fields

assert login_form_fields.get('s_token') is not None
assert self.cookie_dict['cookies'].get('PHPSESSID') is not None

login_data = {
'PHPSESSID': self.cookie_dict['cookies'].get('PHPSESSID'),
's_token': login_form_fields.get('s_token'),
'email': self.email,
'pass': self.password,
'logon': '1',
'seeking': '0', # 0 -, 1 chat only, 2 hookups
'tz': 0, # Force GMT
'win_x': '1080', # Force desktop
'win_y': '720', # Force desktop
}

return login_data

def login(self):
with self.cookie_dict.exclusive_lock():
if self.cookie_dict.get('last_refresh') is None:
log_message = "Creating cookie {}".format(self.cookie_file)
else:
log_message = "Updating cookie that was last refreshed {}".format(
self.cookie_dict.get('last_refresh').humanize())
logger.info(log_message)

r = self.session.post(LOGIN_URL.get(self.email, LOGIN_URL.get("default")),
data=self.login_data)

# Check for HTTP error codes
r.raise_for_status()

# Check for bad URL
if self.email == "guest" and r.url != 'http://dudesnude.com/search_menu.php':
raise Exception('Failure to log in')
if self.email != "guest" and r.url == 'http://dudesnude.com/login.php':
raise Exception('Failure to log in')

self.cookie_dict['cookies'] = self.session.cookies
self.cookie_dict['last_refresh'] = arrow.utcnow()

def handle_relogin(self, r, **kwargs):
"""Handle logging back in if response URL indicates not logged in."""
if not r.url == 'http://dudesnude.com/enable_cookies.php':
return r
self.login()

def __call__(self, req):
"""Handle every call through requests to webpage."""
# Login if cookies dict doesn't exist
if self.cookie_dict.get('last_refresh') is None:
self.login()

# Clean up, since we can't generate new cookies otherwise
if 'Cookie' in req.headers:
del req.headers['Cookie']

# Add saved cookies to request
req.prepare_cookies(self.cookie_dict.get('cookies'))

# Register a method that is called after every request
# to ensure that we don't have to login.
req.register_hook('response', self.handle_relogin)

return req


class DudesNudeClient(object):
def __init__(self, email, password, config_dir=DEFAULT_CFGDIR_BASE):
self._config_dir = ""
self.config_dir = config_dir

#self.guest_session = requests.Session()
#self.guest_session.auth = DudesNudeCookieAuth(email="guest")
self.session = requests.Session()
self.session.auth = DudesNudeCookieAuth(email=email, password=password)

Loading…
Cancel
Save