Example #1
0
 def store_token(self, data):
     Core.debug("storing OAuth token for %r", self.domain)
     self._store_token_libsecret(data)
     try:
         self._store_token_file(data)
     except Exception as e:
         Core.warn("could not write %r: %r", self.token_path, e)
Example #2
0
 def _forget_token(self):
     Core.debug("flushing OAuth tokens")
     nullroute.sec.clear_libsecret({
         "xdg:schema": self.TOKEN_SCHEMA,
         "domain": "pixiv.net"
     })
     os.unlink(self.TOKEN_PATH)
Example #3
0
 def get_illust_info(self, illust_id):
     Core.trace("calling api.works(illust_id=%r)", illust_id)
     resp = self.api.works(illust_id)
     if resp["status"] == "success":
         return resp["response"][0]
     else:
         raise PixivApiError("API call failed: %r" % resp)
Example #4
0
 def get_member_info(self, member_id):
     Core.trace("calling api.users(member_id=%r)", member_id)
     resp = self.api.users(member_id)
     if resp["status"] == "success":
         return resp["response"][0]
     else:
         raise PixivApiError("API call failed: %r" % resp)
Example #5
0
    def increment_attr(self, dn, attr, incr=1, use_increment=True):
        import random
        import time

        if use_increment and \
           self.has_control(OID_LDAP_CONTROL_POSTREAD) and \
           self.has_feature(OID_LDAP_FEATURE_MODIFY_INCREMENT):
            incr = str(incr).encode()
            ctrl = ldap.controls.readentry.PostReadControl(attrList=[attr])
            res = self.conn.modify_ext_s(dn,
                                         [(ldap.MOD_INCREMENT, attr, incr)],
                                         serverctrls=[ctrl])
            for outctrl in res[3]:
                if outctrl.controlType == ctrl.controlType:
                    values = CaseInsensitiveDict(outctrl.entry)[attr]
                    return int(values[0])

        wait = 0
        while True:
            old_val = self.read_attr(dn, attr, raw=True)[0]
            new_val = str(int(old_val) + incr).encode()
            try:
                self.conn.modify_s(dn, [(ldap.MOD_DELETE, attr, old_val),
                                        (ldap.MOD_ADD, attr, new_val)])
                done = True
            except ldap.NO_SUCH_ATTRIBUTE as e:
                Core.debug("swap (%r, %r) failed: %r", old_val, new_val, e)
                wait += 1
                time.sleep(0.05 * 2**random.randint(0, wait))
            else:
                break
        return int(new_val)
Example #6
0
 def _get_json(self, *args, **kwargs):
     resp = self.get(*args, **kwargs)
     resp.raise_for_status()
     data = json.loads(resp.text, object_hook=ObjectDict)
     Core.trace("JSON (%r) = %r", args, data)
     if data.get("error"):
         raise Exception("API error: %r", data.get("message") or data["error"])
     else:
         return data["body"]
Example #7
0
def clear_libsecret(attributes):
    Core.trace("libsecret clear: %r", attributes)
    cmd = ["secret-tool", "clear"]
    for k, v in attributes.items():
        cmd += [str(k), str(v)]

    r = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if r.returncode != 0:
        raise KeyError("libsecret clear failed: %r" % r.stderr.decode())
Example #8
0
    def __init__(self):
        self.ua = requests.Session()
        self.ua.mount("http://", requests.adapters.HTTPAdapter(max_retries=3))
        self.ua.mount("https://", requests.adapters.HTTPAdapter(max_retries=3))

        self.api = pixivpy3.PixivAPI()
        if not hasattr(self.api, "client_secret"):
            Core.warn("this pixivpy3.PixivAPI version does not allow overridding client_secret; OAuth won't work properly")
        self.api.client_id = "MOBrBDS8blbauoSck0ZfDbtuzpyT"
        self.api.client_secret = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj"
        self.api.requests = self.ua
Example #9
0
def get_libsecret(attributes):
    Core.trace("libsecret query: %r", attributes)
    cmd = ["secret-tool", "lookup"]
    for k, v in attributes.items():
        cmd += [str(k), str(v)]

    r = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if r.returncode != 0:
        raise KeyError("libsecret lookup failed: %r" % r.stderr.decode())

    return r.stdout.decode()
Example #10
0
 def _discover_endpoints(self):
     if not self.discovery_url:
         raise ValueError(
             "either discovery URL or endpoint URLs must be specified")
     Core.debug("fetching discovery document %r", self.discovery_url)
     response = urllib.request.urlopen(self.discovery_url).read()
     response = json.loads(response)
     Core.debug("response data: %r", response)
     if not self.authorization_url:
         self.authorization_url = response["authorization_endpoint"]
     if not self.token_grant_url:
         self.token_grant_url = response["token_endpoint"]
Example #11
0
 def load_token(self):
     Core.debug("loading OAuth token for %r", self.domain)
     try:
         return self._load_token_libsecret()
     except KeyError:
         try:
             return self._load_token_file()
         except FileNotFoundError:
             pass
         except Exception as e:
             Core.debug("could not load %r: %r", self.token_path, e)
             self.forget_token()
     return None
Example #12
0
 def _load_member_name_map(self):
     map_path = Env.find_config_file("pixiv_member_names.txt")
     try:
         Core.debug("loading member aliases from %r", map_path)
         with open(map_path, "r") as fh:
             self.member_name_map = {}
             for line in fh:
                 if line.startswith((";", "#", "\n")):
                     continue
                 k, v = line.split("=")
                 self.member_name_map[k.strip()] = v.strip()
     except FileNotFoundError:
         Core.debug("member alias file %r not found; ignoring", map_path)
Example #13
0
def store_libsecret(label, secret, attributes):
    Core.trace("libsecret store: %r %r", label, attributes)
    cmd = ["secret-tool", "store", "--label=%s" % label]
    for k, v in attributes.items():
        cmd += [str(k), str(v)]

    r = subprocess.run(cmd,
                       input=secret.encode(),
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)
    if r.returncode != 0:
        raise IOError("libsecret store failed: (%r, %r)" %
                      (r.returncode, r.stderr.decode()))
Example #14
0
    def __init__(self, url, require_tls=True):
        Core.debug("creating libldap connection to %r", url)
        self.conn = ldap.initialize(url)
        if require_tls and not url.startswith(("ldaps://", "ldapi://")):
            self.conn.start_tls_s()

        self.rootDSE = CaseInsensitiveDict(self.conn.read_rootdse_s())
        self._controls = {
            v.decode()
            for v in self.rootDSE.get("supportedControl", [])
        }
        self._features = {
            v.decode()
            for v in self.rootDSE.get("supportedFeatures", [])
        }
Example #15
0
 def request_tls_certificate(self, domains, csr, years=3):
     Core.debug("posting a ssl_multi_domain order for %r", domains)
     org = self.get_organizations()[0]
     order = {
         "certificate": {
             "common_name": domains[0],
             "csr": csr,
             "dns_names": domains,
             "signature_hash": "sha256",
         },
         "organization": { "id": org["id"] },
         "validity_years": years,
     }
     data = self.post_order("ssl_multi_domain", order)
     return data
Example #16
0
 def _load_token_libsecret(self):
     if self.user_name:
         try:
             data = nullroute.sec.get_libsecret(self.match_fields)
             return json.loads(data)
         except KeyError:
             Core.debug("entry not found; retrying without username")
             old_match_fields = {**self.match_fields}
             del old_match_fields["username"]
             data = nullroute.sec.get_libsecret(old_match_fields)
             Core.debug("migrating entry to add username field")
             nullroute.sec.clear_libsecret(old_match_fields)
             self._store_token_libsecret(json.loads(data))
             return json.loads(data)
     else:
         data = nullroute.sec.get_libsecret(self.match_fields)
         return json.loads(data)
Example #17
0
 def _validate(self):
     Core.debug("verifying session status")
     resp = self.get("https://www.pixiv.net/member.php", allow_redirects=False)
     if resp.is_redirect:
         Core.trace("member.php redirects to %r", resp.next.url)
         url = requests.utils.urlparse(resp.next.url)
         if url.path == "/member.php":
             query = parse_query_string(url.query)
             self.user_id = int(query["id"])
             Core.debug("session is valid, userid %r", self.user_id)
             return True
     Core.debug("session is not valid")
     return False
Example #18
0
 def _store_token(self, token):
     data = {
         "access_token": token.response.access_token,
         "refresh_token": token.response.refresh_token,
         "expires_at": int(time.time() + token.response.expires_in),
         "user_id": token.response.user.id,
     }
     Core.debug("storing OAuth tokens")
     nullroute.sec.store_libsecret("Pixiv OAuth token",
                                   json.dumps(data),
                                   {"xdg:schema": self.TOKEN_SCHEMA,
                                    "domain": "pixiv.net",
                                    "userid": token.response.user.id,
                                    "username": token.response.user.account})
     try:
         with open(self.TOKEN_PATH, "w") as fh:
             json.dump(data, fh)
         return True
     except Exception as e:
         Core.warn("could not write %r: %r", self.TOKEN_PATH, e)
         return False
Example #19
0
    def end_rename(self, new_filename):
        if "/" in new_filename:
            raise ValueError("end_rename() expects only basename, not full path")

        old_path = self.old_path
        new_path = os.path.join(os.path.dirname(old_path), new_filename)
        old_filename = os.path.basename(old_path)

        if old_filename == new_filename:
            print("=>", self.fmt_same % "[no change]")
        elif compare_files(old_path, new_path):
            print("=>", self.fmt_same % new_filename, "[same]")
            if not self.dry_run:
                #os.unlink(old_path)
                gio_trash_file(old_path)
        elif os.path.exists(new_path):
            print("=>", self.fmt_notfound % new_filename, "[diff]")
            Core.err("refusing to overwrite existing file %r", new_filename)
        else:
            print("=>", self.fmt_found % new_filename)
            if not self.dry_run:
                #os.rename(old_path, new_path)
                gio_rename_file(old_path, new_path)
Example #20
0
    def _authenticate(self):
        if self.user_id:
            return True

        psid = os.environ.get("PIXIV_PHPSESSID")
        if psid:
            cookie = requests.cookies.create_cookie(name="PHPSESSID",
                                                    value=psid,
                                                    domain=".pixiv.net")
            cookie.expires = int(time.time() + 3600)
            Core.debug("storing cookie: %r", cookie)
            self._store_token(serialize_cookie(cookie))

        token = self._load_token()
        if token:
            if os.environ.get("FORCE_TOKEN_REFRESH"):
                del os.environ["FORCE_TOKEN_REFRESH"]
                token_valid = False
            else:
                #token_valid = token["expires"] >= time.time()
                # Just pretend the cookie is still valid, as it now comes
                # from the web browser which will keep it active, and we
                # don't really have any way to get a new one anyway.
                token_valid = True
                token["expires"] = int(time.time() + 86400 * 30)

            if token_valid:
                cookie = requests.cookies.create_cookie(**token)
                Core.debug("loaded cookie: %r", cookie)
                self.ua.cookies.set_cookie(cookie)
                if self._validate():
                    cookie.expires = int(time.time() + 86400 * 30)
                    Core.debug("updating cookie: %r", cookie)
                    self._store_token(serialize_cookie(cookie))
                    return True
            else:
                Core.debug("cookie has expired")

        raise Exception("Pixiv cookie not found or expired")
Example #21
0
 def _grant_token(self, params):
     if not self.token_grant_url:
         self._discover_endpoints()
     Core.debug("token grant URL: %r", self.token_grant_url)
     post_data = {
         "client_id": self.client_id,
         "client_secret": self.client_secret,
         **params
     }
     Core.debug("request data: %r", post_data)
     post_data = urllib.parse.urlencode(post_data).encode()
     Core.debug("encoded data: %r", post_data)
     response = urllib.request.urlopen(self.token_grant_url,
                                       post_data).read()
     response = json.loads(response)
     response.setdefault("expires_at",
                         int(time.time() + response["expires_in"]))
     return response
Example #22
0
 def _load_token(self):
     try:
         data = nullroute.sec.get_libsecret({"xdg:schema": self.TOKEN_SCHEMA,
                                             "domain": "pixiv.net"})
         Core.debug("found OAuth token in keyring")
         return json.loads(data)
     except KeyError:
         try:
             with open(self.TOKEN_PATH, "r") as fh:
                 data = json.load(fh)
             Core.debug("found OAuth token in filesystem")
             return data
         except FileNotFoundError:
             pass
         except Exception as e:
             Core.debug("could not load %r: %r", self.TOKEN_PATH, e)
             self._forget_token()
     return None
Example #23
0
 def forget_token(self):
     Core.debug("flushing OAuth tokens for %r", self.domain)
     self._clear_token_libsecret()
     self._clear_token_file()
Example #24
0
 def post(self, ep, *args, **kwargs):
     uri = self.base + ep
     Core.debug("posting to %r" % uri)
     resp = self.ua.post(uri, *args, **kwargs)
     resp.raise_for_status()
     return resp
Example #25
0
 def get(self, ep, *args, **kwargs):
     uri = self.base + ep
     Core.debug("fetching %r" % uri)
     resp = self.ua.get(uri, *args, **kwargs)
     resp.raise_for_status()
     return resp
Example #26
0
    def _authenticate(self):
        if self.api.user_id:
            Core.warn("BUG: _authenticate() called twice")
            return True

        data = self._load_token()
        if data:
            Core.trace("loaded token: %r", data)
            if os.environ.get("FORCE_TOKEN_REFRESH"):
                token_valid = False
            else:
                token_valid = data["expires_at"] > time.time()

            if token_valid:
                Core.debug("access token within expiry time, using as-is")
                self.api.user_id = data["user_id"]
                self.api.access_token = data["access_token"]
                self.api.refresh_token = data["refresh_token"]
                return True
            else:
                Core.debug("access token has expired, renewing")
                try:
                    token = self.api.auth(refresh_token=data["refresh_token"])
                    Core.trace("retrieved token: %r", token)
                except Exception as e:
                    Core.warn("could not refresh access token: %r", e)
                    #self._forget_token()
                else:
                    self._store_token(token)
                    return True

        data = self._load_creds()
        if data:
            Core.info("logging in to Pixiv as %r", data["login"])
            try:
                token = self.api.auth(username=data["login"],
                                      password=data["password"])
            except Exception as e:
                Core.warn("could not log in using username & password: %r", e)
            else:
                self._store_token(token)
                return True

        Core.die("could not log in to Pixiv (no credentials)")
        return False
Example #27
0
# https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-00


def hash_data(hash_algo, data):
    if hash_algo in {"sha1", "sha256", "sha512"}:
        return hashlib.new(hash_algo, data).digest()
    else:
        raise UnsupportedHashType(hash_algo)


rest = sys.argv[1:]

if rest:
    cmd, *rest = rest
else:
    Core.die("missing command (try 'sign' or 'verify')")

if cmd == "sign":
    _ap = argparse.ArgumentParser()
    _ap.add_argument(
        "--fingerprint",
        help="Key fingerprint in 'MD5:<hex>' or 'SHA256:<b64>' format")
    _ap.add_argument("--input-hexdata")
    _ap.add_argument("--input-string")
    _ap.add_argument("--test-verify", action="store_true")
    _ap.add_argument("--namespace")
    args = _ap.parse_args(rest)

    if not args.fingerprint:
        Core.die("signing key (--fingerprint) not specified")
Example #28
0
 def end_fail(self, e):
     print(self.fmt_notfound % "failed")
     Core.err(str(e))