summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask/util')
-rw-r--r--src/leap/bitmask/util/__init__.py43
-rw-r--r--src/leap/bitmask/util/constants.py1
-rw-r--r--src/leap/bitmask/util/keyring_helpers.py65
-rw-r--r--src/leap/bitmask/util/leap_argparse.py81
-rw-r--r--src/leap/bitmask/util/leap_log_handler.py7
-rw-r--r--src/leap/bitmask/util/log_silencer.py1
-rwxr-xr-xsrc/leap/bitmask/util/pastebin.py814
7 files changed, 975 insertions, 37 deletions
diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py
index b58e6e3b..2b2cd874 100644
--- a/src/leap/bitmask/util/__init__.py
+++ b/src/leap/bitmask/util/__init__.py
@@ -18,19 +18,28 @@
Some small and handy functions.
"""
import datetime
+import itertools
import os
from leap.bitmask.config import flags
from leap.common.config import get_path_prefix as common_get_path_prefix
+# functional goodies for a healthier life:
+# We'll give your money back if it does not alleviate the eye strain, at least.
-def get_path_prefix():
- return common_get_path_prefix(flags.STANDALONE)
+
+# levelname length == 8, since 'CRITICAL' is the longest
+LOG_FORMAT = ('%(asctime)s - %(levelname)-8s - '
+ 'L#%(lineno)-4s : %(name)s:%(funcName)s() - %(message)s')
def first(things):
"""
Return the head of a collection.
+
+ :param things: a sequence to extract the head from.
+ :type things: sequence
+ :return: object, or None
"""
try:
return things[0]
@@ -38,6 +47,23 @@ def first(things):
return None
+def flatten(things):
+ """
+ Return a generator iterating through a flattened sequence.
+
+ :param things: a nested sequence, eg, a list of lists.
+ :type things: sequence
+ :rtype: generator
+ """
+ return itertools.chain(*things)
+
+
+# leap repetitive chores
+
+def get_path_prefix():
+ return common_get_path_prefix(flags.STANDALONE)
+
+
def get_modification_ts(path):
"""
Gets modification time of a file.
@@ -76,3 +102,16 @@ def is_empty_file(path):
Returns True if the file at path is empty.
"""
return os.stat(path).st_size is 0
+
+
+def make_address(user, provider):
+ """
+ Return a full identifier for an user, as a email-like
+ identifier.
+
+ :param user: the username
+ :type user: basestring
+ :param provider: the provider domain
+ :type provider: basestring
+ """
+ return "%s@%s" % (user, provider)
diff --git a/src/leap/bitmask/util/constants.py b/src/leap/bitmask/util/constants.py
index e6a6bdce..e7e72cc4 100644
--- a/src/leap/bitmask/util/constants.py
+++ b/src/leap/bitmask/util/constants.py
@@ -17,3 +17,4 @@
SIGNUP_TIMEOUT = 5
REQUEST_TIMEOUT = 15
+PASTEBIN_API_DEV_KEY = "09563100642af6085d641f749a1922b4"
diff --git a/src/leap/bitmask/util/keyring_helpers.py b/src/leap/bitmask/util/keyring_helpers.py
index 4b3eb57f..ee2d7a1c 100644
--- a/src/leap/bitmask/util/keyring_helpers.py
+++ b/src/leap/bitmask/util/keyring_helpers.py
@@ -19,30 +19,67 @@ Keyring helpers.
"""
import logging
-import keyring
+try:
+ import keyring
+ from keyring.backends.file import EncryptedKeyring, PlaintextKeyring
+ OBSOLETE_KEYRINGS = [
+ EncryptedKeyring,
+ PlaintextKeyring
+ ]
+ canuse = lambda kr: (kr is not None
+ and kr.__class__ not in OBSOLETE_KEYRINGS)
+
+except Exception:
+ # Problems when importing keyring! It might be a problem binding to the
+ # dbus socket, or stuff like that.
+ keyring = None
-from keyring.backends.file import EncryptedKeyring, PlaintextKeyring
logger = logging.getLogger(__name__)
-OBSOLETE_KEYRINGS = [
- EncryptedKeyring,
- PlaintextKeyring
-]
+def _get_keyring_with_fallback():
+ """
+ Get the default keyring, and if obsolete try to pick SecretService keyring
+ if available.
+
+ This is a workaround for the cases in which the keyring module chooses
+ an insecure keyring by default (ie, inside a virtualenv).
+ """
+ if not keyring:
+ return None
+ kr = keyring.get_keyring()
+ if not canuse(kr):
+ try:
+ kr_klass = keyring.backends.SecretService
+ kr = kr_klass.Keyring()
+ except AttributeError:
+ logger.warning("Keyring cannot find SecretService Backend")
+ logger.debug("Selected keyring: %s" % (kr.__class__,))
+ if not canuse(kr):
+ logger.debug("Not using default keyring since it is obsolete")
+ return kr
def has_keyring():
"""
- Returns whether we have an useful keyring to use.
+ Return whether we have an useful keyring to use.
:rtype: bool
"""
- kr = keyring.get_keyring()
- klass = kr.__class__
- logger.debug("Selected keyring: %s" % (klass,))
+ if not keyring:
+ return False
+ kr = _get_keyring_with_fallback()
+ return canuse(kr)
+
- canuse = kr is not None and klass not in OBSOLETE_KEYRINGS
- if not canuse:
- logger.debug("Not using this keyring since it is obsolete")
- return canuse
+def get_keyring():
+ """
+ Return an usable keyring.
+
+ :rtype: keyringBackend or None
+ """
+ if not keyring:
+ return False
+ kr = _get_keyring_with_fallback()
+ return kr if canuse(kr) else None
diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py
index e8a9fda9..88267ff8 100644
--- a/src/leap/bitmask/util/leap_argparse.py
+++ b/src/leap/bitmask/util/leap_argparse.py
@@ -27,30 +27,77 @@ def build_parser():
All the options for the leap arg parser
Some of these could be switched on only if debug flag is present!
"""
- epilog = "Copyright 2012-2013 The LEAP Encryption Access Project"
- parser = argparse.ArgumentParser(description="""
-Launches Bitmask""", epilog=epilog)
+ parser = argparse.ArgumentParser(
+ description="Launches the Bitmask client.",
+ epilog="Copyright 2012-2014 The LEAP Encryption Access Project")
+
parser.add_argument('-d', '--debug', action="store_true",
- help=("Launches Bitmask in debug mode, writing debug"
- "info to stdout"))
- if not IS_RELEASE_VERSION:
- help_text = "Bypasses the certificate check for bootstrap"
- parser.add_argument('--danger', action="store_true", help=help_text)
+ help=("Launches Bitmask in debug mode, writing debug "
+ "info to stdout."))
+ parser.add_argument('-V', '--version', action="store_true",
+ help='Displays Bitmask version and exits.')
+ # files
parser.add_argument('-l', '--logfile', metavar="LOG FILE", nargs='?',
action="store", dest="log_file",
- #type=argparse.FileType('w'),
- help='optional log file')
+ help='Optional log file.')
+ parser.add_argument('-m', '--mail-logfile',
+ metavar="MAIL LOG FILE", nargs='?',
+ action="store", dest="mail_log_file",
+ help='Optional log file for email.')
+
+ # flags
+ parser.add_argument('-s', '--standalone', action="store_true",
+ help='Makes Bitmask use standalone '
+ 'directories for configuration and binary '
+ 'searching.')
+ parser.add_argument('-N', '--no-app-version-check', default=True,
+ action="store_false", dest="app_version_check",
+ help='Skip the app version compatibility check with '
+ 'the provider.')
+ parser.add_argument('-M', '--no-api-version-check', default=True,
+ action="store_false", dest="api_version_check",
+ help='Skip the api version compatibility check with '
+ 'the provider.')
+
+ # openvpn options
parser.add_argument('--openvpn-verbosity', nargs='?',
type=int,
action="store", dest="openvpn_verb",
- help='verbosity level for openvpn logs [1-6]')
- parser.add_argument('-s', '--standalone', action="store_true",
- help='Makes Bitmask use standalone'
- 'directories for configuration and binary'
- 'searching')
- parser.add_argument('-V', '--version', action="store_true",
- help='Displays Bitmask version and exits')
+ help='Verbosity level for openvpn logs [1-6]')
+
+ # mail stuff
+ parser.add_argument('-o', '--offline', action="store_true",
+ help='Starts Bitmask in offline mode: will not '
+ 'try to sync with remote replicas for email.')
+
+ parser.add_argument('--acct', metavar="user@provider",
+ nargs='?',
+ action="store", dest="acct",
+ help='Manipulate mailboxes for this account')
+ parser.add_argument('-r', '--repair-mailboxes', default=False,
+ action="store_true", dest="repair",
+ help='Repair mailboxes for a given account. '
+ 'Use when upgrading versions after a schema '
+ 'change. Use with --acct')
+ parser.add_argument('--import-maildir', metavar="/path/to/Maildir",
+ nargs='?',
+ action="store", dest="import_maildir",
+ help='Import the given maildir. Use with the '
+ '--to-mbox flag to import to folders other '
+ 'than INBOX. Use with --acct')
+
+ if not IS_RELEASE_VERSION:
+ help_text = ("Bypasses the certificate check during provider "
+ "bootstraping, for debugging development servers. "
+ "Use at your own risk!")
+ parser.add_argument('--danger', action="store_true", help=help_text)
+
+ # optional cert file used to check domains with self signed certs.
+ parser.add_argument('--ca-cert-file', metavar="/path/to/cacert.pem",
+ nargs='?', action="store", dest="ca_cert_file",
+ help='Uses the given cert file to verify '
+ 'against domains.')
# Not in use, we might want to reintroduce them.
#parser.add_argument('-i', '--no-provider-checks',
diff --git a/src/leap/bitmask/util/leap_log_handler.py b/src/leap/bitmask/util/leap_log_handler.py
index 1ab62331..807e53d4 100644
--- a/src/leap/bitmask/util/leap_log_handler.py
+++ b/src/leap/bitmask/util/leap_log_handler.py
@@ -21,6 +21,8 @@ import logging
from PySide import QtCore
+from leap.bitmask.util import LOG_FORMAT
+
class LogHandler(logging.Handler):
"""
@@ -52,10 +54,7 @@ class LogHandler(logging.Handler):
:param logging_level: the debug level to define the color.
:type logging_level: str.
"""
- log_format = ('%(asctime)s - %(name)s:%(funcName)s:L#%(lineno)s '
- '- %(levelname)s - %(message)s')
- formatter = logging.Formatter(log_format)
-
+ formatter = logging.Formatter(LOG_FORMAT)
return formatter
def emit(self, logRecord):
diff --git a/src/leap/bitmask/util/log_silencer.py b/src/leap/bitmask/util/log_silencer.py
index b9f69ad2..56b290e4 100644
--- a/src/leap/bitmask/util/log_silencer.py
+++ b/src/leap/bitmask/util/log_silencer.py
@@ -46,6 +46,7 @@ class SelectiveSilencerFilter(logging.Filter):
# to us.
SILENCER_RULES = (
'leap.common.events',
+ 'leap.common.decorators',
)
def __init__(self):
diff --git a/src/leap/bitmask/util/pastebin.py b/src/leap/bitmask/util/pastebin.py
new file mode 100755
index 00000000..21b8a0b7
--- /dev/null
+++ b/src/leap/bitmask/util/pastebin.py
@@ -0,0 +1,814 @@
+#!/usr/bin/env python
+
+#############################################################################
+# Pastebin.py - Python 3.2 Pastebin API.
+# Copyright (C) 2012 Ian Havelock
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+#############################################################################
+
+# This software is a derivative work of:
+# http://winappdbg.sourceforge.net/blog/pastebin.py
+
+#############################################################################
+
+
+__ALL__ = ['delete_paste', 'user_details', 'trending', 'pastes_by_user',
+ 'generate_user_key', 'legacy_paste', 'paste', 'Pastebin',
+ 'PastebinError']
+
+import urllib
+
+
+class PastebinError(RuntimeError):
+ """Pastebin API error.
+
+ The error message returned by the web application is stored as the Python
+ exception message."""
+
+
+class PastebinAPI(object):
+ """Pastebin API interaction object.
+
+ Public functions:
+
+ paste -- Pastes a user-specified file or string using the new API-key POST
+ method.
+
+ legacy_paste -- Pastes a user-specified file or string using the old
+ anonymous POST method.
+
+ generate_user_key -- Generates a session-key that is required for other
+ functions.
+
+ pastes_by_user -- Returns all public pastes submitted by the specified
+ login credentials.
+
+ trending -- Returns the top trending paste.
+
+ user_details -- Returns details about the user for the specified API user
+ key.
+
+ delete_paste -- Adds two numbers together and returns the result."""
+
+ # String to determine bad API requests
+ _bad_request = 'Bad API request'
+
+ # Base domain name
+ _base_domain = 'pastebin.com'
+
+ # Valid Pastebin URLs begin with this string (kinda obvious)
+ _prefix_url = 'http://%s/' % _base_domain
+
+ # Valid Pastebin URLs with a custom subdomain begin with this string
+ _subdomain_url = 'http://%%s.%s/' % _base_domain
+
+ # URL to the LEGACY POST API
+ _legacy_api_url = 'http://%s/api_public.php' % _base_domain
+
+ # URL to the POST API
+ _api_url = 'http://%s/api/api_post.php' % _base_domain
+
+ # URL to the login POST API
+ _api_login_url = 'http://%s/api/api_login.php' % _base_domain
+
+ # Valid paste_expire_date values: Never, 10 minutes, 1 Hour, 1 Day, 1 Month
+ paste_expire_date = ('N', '10M', '1H', '1D', '1M')
+
+ # Valid paste_expire_date values (0 = public, 1 = unlisted, 2 = private)
+ paste_private = ('public', 'unlisted', 'private')
+
+ # Valid parse_format values
+ paste_format = (
+ '4cs', # 4CS
+ '6502acme', # 6502 ACME Cross Assembler
+ '6502kickass', # 6502 Kick Assembler
+ '6502tasm', # 6502 TASM/64TASS
+ 'abap', # ABAP
+ 'actionscript', # ActionScript
+ 'actionscript3', # ActionScript 3
+ 'ada', # Ada
+ 'algol68', # ALGOL 68
+ 'apache', # Apache Log
+ 'applescript', # AppleScript
+ 'apt_sources', # APT Sources
+ 'asm', # ASM (NASM)
+ 'asp', # ASP
+ 'autoconf', # autoconf
+ 'autohotkey', # Autohotkey
+ 'autoit', # AutoIt
+ 'avisynth', # Avisynth
+ 'awk', # Awk
+ 'bascomavr', # BASCOM AVR
+ 'bash', # Bash
+ 'basic4gl', # Basic4GL
+ 'bibtex', # BibTeX
+ 'blitzbasic', # Blitz Basic
+ 'bnf', # BNF
+ 'boo', # BOO
+ 'bf', # BrainFuck
+ 'c', # C
+ 'c_mac', # C for Macs
+ 'cil', # C Intermediate Language
+ 'csharp', # C#
+ 'cpp', # C++
+ 'cpp-qt', # C++ (with QT extensions)
+ 'c_loadrunner', # C: Loadrunner
+ 'caddcl', # CAD DCL
+ 'cadlisp', # CAD Lisp
+ 'cfdg', # CFDG
+ 'chaiscript', # ChaiScript
+ 'clojure', # Clojure
+ 'klonec', # Clone C
+ 'klonecpp', # Clone C++
+ 'cmake', # CMake
+ 'cobol', # COBOL
+ 'coffeescript', # CoffeeScript
+ 'cfm', # ColdFusion
+ 'css', # CSS
+ 'cuesheet', # Cuesheet
+ 'd', # D
+ 'dcs', # DCS
+ 'delphi', # Delphi
+ 'oxygene', # Delphi Prism (Oxygene)
+ 'diff', # Diff
+ 'div', # DIV
+ 'dos', # DOS
+ 'dot', # DOT
+ 'e', # E
+ 'ecmascript', # ECMAScript
+ 'eiffel', # Eiffel
+ 'email', # Email
+ 'epc', # EPC
+ 'erlang', # Erlang
+ 'fsharp', # F#
+ 'falcon', # Falcon
+ 'fo', # FO Language
+ 'f1', # Formula One
+ 'fortran', # Fortran
+ 'freebasic', # FreeBasic
+ 'freeswitch', # FreeSWITCH
+ 'gambas', # GAMBAS
+ 'gml', # Game Maker
+ 'gdb', # GDB
+ 'genero', # Genero
+ 'genie', # Genie
+ 'gettext', # GetText
+ 'go', # Go
+ 'groovy', # Groovy
+ 'gwbasic', # GwBasic
+ 'haskell', # Haskell
+ 'hicest', # HicEst
+ 'hq9plus', # HQ9 Plus
+ 'html4strict', # HTML
+ 'html5', # HTML 5
+ 'icon', # Icon
+ 'idl', # IDL
+ 'ini', # INI file
+ 'inno', # Inno Script
+ 'intercal', # INTERCAL
+ 'io', # IO
+ 'j', # J
+ 'java', # Java
+ 'java5', # Java 5
+ 'javascript', # JavaScript
+ 'jquery', # jQuery
+ 'kixtart', # KiXtart
+ 'latex', # Latex
+ 'lb', # Liberty BASIC
+ 'lsl2', # Linden Scripting
+ 'lisp', # Lisp
+ 'llvm', # LLVM
+ 'locobasic', # Loco Basic
+ 'logtalk', # Logtalk
+ 'lolcode', # LOL Code
+ 'lotusformulas', # Lotus Formulas
+ 'lotusscript', # Lotus Script
+ 'lscript', # LScript
+ 'lua', # Lua
+ 'm68k', # M68000 Assembler
+ 'magiksf', # MagikSF
+ 'make', # Make
+ 'mapbasic', # MapBasic
+ 'matlab', # MatLab
+ 'mirc', # mIRC
+ 'mmix', # MIX Assembler
+ 'modula2', # Modula 2
+ 'modula3', # Modula 3
+ '68000devpac', # Motorola 68000 HiSoft Dev
+ 'mpasm', # MPASM
+ 'mxml', # MXML
+ 'mysql', # MySQL
+ 'newlisp', # newLISP
+ 'text', # None
+ 'nsis', # NullSoft Installer
+ 'oberon2', # Oberon 2
+ 'objeck', # Objeck Programming Langua
+ 'objc', # Objective C
+ 'ocaml-brief', # OCalm Brief
+ 'ocaml', # OCaml
+ 'pf', # OpenBSD PACKET FILTER
+ 'glsl', # OpenGL Shading
+ 'oobas', # Openoffice BASIC
+ 'oracle11', # Oracle 11
+ 'oracle8', # Oracle 8
+ 'oz', # Oz
+ 'pascal', # Pascal
+ 'pawn', # PAWN
+ 'pcre', # PCRE
+ 'per', # Per
+ 'perl', # Perl
+ 'perl6', # Perl 6
+ 'php', # PHP
+ 'php-brief', # PHP Brief
+ 'pic16', # Pic 16
+ 'pike', # Pike
+ 'pixelbender', # Pixel Bender
+ 'plsql', # PL/SQL
+ 'postgresql', # PostgreSQL
+ 'povray', # POV-Ray
+ 'powershell', # Power Shell
+ 'powerbuilder', # PowerBuilder
+ 'proftpd', # ProFTPd
+ 'progress', # Progress
+ 'prolog', # Prolog
+ 'properties', # Properties
+ 'providex', # ProvideX
+ 'purebasic', # PureBasic
+ 'pycon', # PyCon
+ 'python', # Python
+ 'q', # q/kdb+
+ 'qbasic', # QBasic
+ 'rsplus', # R
+ 'rails', # Rails
+ 'rebol', # REBOL
+ 'reg', # REG
+ 'robots', # Robots
+ 'rpmspec', # RPM Spec
+ 'ruby', # Ruby
+ 'gnuplot', # Ruby Gnuplot
+ 'sas', # SAS
+ 'scala', # Scala
+ 'scheme', # Scheme
+ 'scilab', # Scilab
+ 'sdlbasic', # SdlBasic
+ 'smalltalk', # Smalltalk
+ 'smarty', # Smarty
+ 'sql', # SQL
+ 'systemverilog', # SystemVerilog
+ 'tsql', # T-SQL
+ 'tcl', # TCL
+ 'teraterm', # Tera Term
+ 'thinbasic', # thinBasic
+ 'typoscript', # TypoScript
+ 'unicon', # Unicon
+ 'uscript', # UnrealScript
+ 'vala', # Vala
+ 'vbnet', # VB.NET
+ 'verilog', # VeriLog
+ 'vhdl', # VHDL
+ 'vim', # VIM
+ 'visualprolog', # Visual Pro Log
+ 'vb', # VisualBasic
+ 'visualfoxpro', # VisualFoxPro
+ 'whitespace', # WhiteSpace
+ 'whois', # WHOIS
+ 'winbatch', # Winbatch
+ 'xbasic', # XBasic
+ 'xml', # XML
+ 'xorg_conf', # Xorg Config
+ 'xpp', # XPP
+ 'yaml', # YAML
+ 'z80', # Z80 Assembler
+ 'zxbasic', # ZXBasic
+ )
+
+ def __init__(self):
+ pass
+
+ def delete_paste(self, api_dev_key, api_user_key, api_paste_key):
+ """Delete the paste specified by the api_paste_key.
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ paste_to_delete = x.delete_paste(
+ '453a994e0e2f1efae07f8759e59e075b',
+ 'c57a18e6c0ae228cd4bd16fe36da381a',
+ 'WkgcTFtv')
+ print paste_to_delete
+ Paste Removed
+
+
+ @type api_dev_key: string
+ @param api_dev_key: The API Developer Key of a registered
+ U{http://pastebin.com} account.
+
+ @type api_user_key: string
+ @param api_user_key: The API User Key of a U{http://pastebin.com}
+ registered user.
+
+ @type api_paste_key: string
+ @param api_paste_key: The Paste Key of the paste to be deleted
+ (string after final / in
+ U{http://pastebin.com} URL).
+
+ @rtype: string
+ @returns: A successful deletion returns 'Paste Removed'.
+ """
+
+ # Valid api developer key
+ argv = {'api_dev_key': str(api_dev_key)}
+
+ # Requires pre-registered account
+ if api_user_key is not None:
+ argv['api_user_key'] = str(api_user_key)
+
+ # Key of the paste to be deleted.
+ if api_paste_key is not None:
+ argv['api_paste_key'] = str(api_paste_key)
+
+ # Valid API option - 'user_details' in this instance
+ argv['api_option'] = str('delete')
+
+ # lets try to read the URL that we've just built.
+ request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv))
+ response = request_string.read()
+
+ return response
+
+ def user_details(self, api_dev_key, api_user_key):
+ """Return user details of the user specified by the api_user_key.
+
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ details = x.user_details('453a994e0e2f1efae07f8759e59e075b', 'c57a18e6c0ae228cd4bd16fe36da381a')
+ print details
+ <user>
+ <user_name>MonkeyPuzzle</user_name>
+ <user_format_short>python</user_format_short>
+ <user_expiration>N</user_expiration>
+ <user_avatar_url>http://pastebin.com/i/guest.gif</user_avatar_url>
+ <user_private>0</user_private>
+ <user_website></user_website>
+ <user_email>user@email.com</user_email>
+ <user_location></user_location>
+ <user_account_type>0</user_account_type>
+ </user>
+
+
+ @type api_dev_key: string
+ @param api_dev_key: The API Developer Key of a registered
+ U{http://pastebin.com} account.
+
+ @type api_user_key: string
+ @param api_user_key: The API User Key of a U{http://pastebin.com}
+ registered user.
+
+ @rtype: string
+ @returns: Returns an XML string containing user information.
+ """
+
+ # Valid api developer key
+ argv = {'api_dev_key': str(api_dev_key)}
+
+ # Requires pre-registered account to generate an api_user_key
+ # (see generate_user_key)
+ if api_user_key is not None:
+ argv['api_user_key'] = str(api_user_key)
+
+ # Valid API option - 'user_details' in this instance
+ argv['api_option'] = str('userdetails')
+
+ # lets try to read the URL that we've just built.
+ request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv))
+ response = request_string.read()
+
+ # do some basic error checking here so we can gracefully handle any
+ # errors we are likely to encounter
+ if response.startswith(self._bad_request):
+ raise PastebinError(response)
+
+ elif not response.startswith('<user>'):
+ raise PastebinError(response)
+
+ return response
+
+ def trending(self, api_dev_key):
+ """Returns the top trending paste details.
+
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ details = x.trending('453a994e0e2f1efae07f8759e59e075b')
+ print details
+ <paste>
+ <paste_key>jjMRFDH6</paste_key>
+ <paste_date>1333230838</paste_date>
+ <paste_title></paste_title>
+ <paste_size>6416</paste_size>
+ <paste_expire_date>0</paste_expire_date>
+ <paste_private>0</paste_private>
+ <paste_format_long>None</paste_format_long>
+ <paste_format_short>text</paste_format_short>
+ <paste_url>http://pastebin.com/jjMRFDH6</paste_url>
+ <paste_hits>6384</paste_hits>
+ </paste>
+
+ Note: Returns multiple trending pastes, not just 1.
+
+
+ @type api_dev_key: string
+ @param api_dev_key: The API Developer Key of a registered
+ U{http://pastebin.com} account.
+
+
+ @rtype: string
+ @return: Returns the string (XML formatted) containing the top
+ trending pastes.
+ """
+
+ # Valid api developer key
+ argv = {'api_dev_key': str(api_dev_key), 'api_option': str('trends')}
+
+ # Valid API option - 'trends' is returns trending pastes
+
+ # lets try to read the URL that we've just built.
+ request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv))
+ response = request_string.read()
+
+ # do some basic error checking here so we can gracefully handle any
+ # errors we are likely to encounter
+ if response.startswith(self._bad_request):
+ raise PastebinError(response)
+
+ elif not response.startswith('<paste>'):
+ raise PastebinError(response)
+
+ return response
+
+ def pastes_by_user(self, api_dev_key, api_user_key, results_limit=None):
+ """Returns all pastes for the provided api_user_key.
+
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ details = x.user_details('453a994e0e2f1efae07f8759e59e075b',
+ 'c57a18e6c0ae228cd4bd16fe36da381a',
+ 100)
+ print details
+ <paste>
+ <paste_key>DLiSspYT</paste_key>
+ <paste_date>1332714730</paste_date>
+ <paste_title>Pastebin.py - Python 3.2 Pastebin.com API</paste_title>
+ <paste_size>25300</paste_size>
+ <paste_expire_date>0</paste_expire_date>
+ <paste_private>0</paste_private>
+ <paste_format_long>Python</paste_format_long>
+ <paste_format_short>python</paste_format_short>
+ <paste_url>http://pastebin.com/DLiSspYT</paste_url>
+ <paste_hits>70</paste_hits>
+ </paste>
+
+ Note: Returns multiple pastes, not just 1.
+
+
+ @type api_dev_key: string
+ @param api_dev_key: The API Developer Key of a registered
+ U{http://pastebin.com} account.
+
+ @type api_user_key: string
+ @param api_user_key: The API User Key of a U{http://pastebin.com}
+ registered user.
+
+ @type results_limit: number
+ @param results_limit: The number of pastes to return between 1 - 1000.
+
+ @rtype: string
+ @returns: Returns an XML string containing number of specified pastes
+ by user.
+ """
+
+ # Valid api developer key
+ argv = {'api_dev_key': str(api_dev_key)}
+
+ # Requires pre-registered account
+ if api_user_key is not None:
+ argv['api_user_key'] = str(api_user_key)
+
+ # Number of results to return - between 1 & 1000, default = 50
+ if results_limit is None:
+ argv['api_results_limit'] = 50
+
+ if results_limit is not None:
+ if results_limit < 1:
+ argv['api_results_limit'] = 50
+ elif results_limit > 1000:
+ argv['api_results_limit'] = 1000
+ else:
+ argv['api_results_limit'] = int(results_limit)
+
+ # Valid API option - 'paste' is default for new paste
+ argv['api_option'] = str('list')
+
+ # lets try to read the URL that we've just built.
+ request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv))
+ response = request_string.read()
+
+ # do some basic error checking here so we can gracefully handle any
+ # errors we are likely to encounter
+ if response.startswith(self._bad_request):
+ raise PastebinError(response)
+
+ elif not response.startswith('<paste>'):
+ raise PastebinError(response)
+
+ return response
+
+ def generate_user_key(self, api_dev_key, username, password):
+ """Generate a user session key - needed for other functions.
+
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ my_key = x.generate_user_key(
+ '453a994e0e2f1efae07f8759e59e075b',
+ 'MonkeyPuzzle',
+ '12345678')
+ print my_key
+ c57a18e6c0ae228cd4bd16fe36da381a
+
+
+ @type api_dev_key: string
+ @param api_dev_key: The API Developer Key of a registered
+ U{http://pastebin.com} account.
+
+ @type username: string
+ @param username: The username of a registered U{http://pastebin.com}
+ account.
+
+ @type password: string
+ @param password: The password of a registered U{http://pastebin.com}
+ account.
+
+ @rtype: string
+ @returns: Session key (api_user_key) to allow authenticated
+ interaction to the API.
+
+ """
+ # Valid api developer key
+ argv = {'api_dev_key': str(api_dev_key)}
+
+ # Requires pre-registered pastebin account
+ if username is not None:
+ argv['api_user_name'] = str(username)
+
+ # Requires pre-registered pastebin account
+ if password is not None:
+ argv['api_user_password'] = str(password)
+
+ # lets try to read the URL that we've just built.
+ data = urllib.urlencode(argv)
+ request_string = urllib.urlopen(self._api_login_url, data)
+ response = request_string.read()
+
+ # do some basic error checking here so we can gracefully handle
+ # any errors we are likely to encounter
+ if response.startswith(self._bad_request):
+ raise PastebinError(response)
+
+ return response
+
+ def paste(self, api_dev_key, api_paste_code,
+ api_user_key=None, paste_name=None, paste_format=None,
+ paste_private=None, paste_expire_date=None):
+
+ """Submit a code snippet to Pastebin using the new API.
+
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ url = x.paste(
+ '453a994e0e2f1efae07f8759e59e075b' ,
+ 'Snippet of code to paste goes here',
+ paste_name = 'title of paste',
+ api_user_key = 'c57a18e6c0ae228cd4bd16fe36da381a',
+ paste_format = 'python',
+ paste_private = 'unlisted',
+ paste_expire_date = '10M')
+ print url
+ http://pastebin.com/tawPUgqY
+
+
+ @type api_dev_key: string
+ @param api_dev_key: The API Developer Key of a registered
+ U{http://pastebin.com} account.
+
+ @type api_paste_code: string
+ @param api_paste_code: The file or string to paste to body of the
+ U{http://pastebin.com} paste.
+
+ @type api_user_key: string
+ @param api_user_key: The API User Key of a U{http://pastebin.com}
+ registered user.
+ If none specified, paste is made as a guest.
+
+ @type paste_name: string
+ @param paste_name: (Optional) Title of the paste.
+ Default is to paste anonymously.
+
+ @type paste_format: string
+ @param paste_format: (Optional) Programming language of the code being
+ pasted. This enables syntax highlighting when reading the code in
+ U{http://pastebin.com}. Default is no syntax highlighting (text is
+ just text and not source code).
+
+ @type paste_private: string
+ @param paste_private: (Optional) C{'public'} if the paste is public
+ (visible by everyone), C{'unlisted'} if it's public but not
+ searchable. C{'private'} if the paste is private and not
+ searchable or indexed.
+ The Pastebin FAQ (U{http://pastebin.com/faq}) claims
+ private pastes are not indexed by search engines (aka Google).
+
+ @type paste_expire_date: str
+ @param paste_expire_date: (Optional) Expiration date for the paste.
+ Once past this date the paste is deleted automatically. Valid
+ values are found in the L{PastebinAPI.paste_expire_date} class
+ member.
+ If not provided, the paste never expires.
+
+ @rtype: string
+ @return: Returns the URL to the newly created paste.
+ """
+
+ # Valid api developer key
+ argv = {'api_dev_key': str(api_dev_key)}
+
+ # Code snippet to submit
+ if api_paste_code is not None:
+ argv['api_paste_code'] = str(api_paste_code)
+
+ # Valid API option - 'paste' is default for new paste
+ argv['api_option'] = str('paste')
+
+ # API User Key
+ if api_user_key is not None:
+ argv['api_user_key'] = str(api_user_key)
+ elif api_user_key is None:
+ argv['api_user_key'] = str('')
+
+ # Name of the poster
+ if paste_name is not None:
+ argv['api_paste_name'] = str(paste_name)
+
+ # Syntax highlighting
+ if paste_format is not None:
+ paste_format = str(paste_format).strip().lower()
+ argv['api_paste_format'] = paste_format
+
+ # Is the snippet private?
+ if paste_private is not None:
+ if paste_private == 'public':
+ argv['api_paste_private'] = int(0)
+ elif paste_private == 'unlisted':
+ argv['api_paste_private'] = int(1)
+ elif paste_private == 'private':
+ argv['api_paste_private'] = int(2)
+
+ # Expiration for the snippet
+ if paste_expire_date is not None:
+ paste_expire_date = str(paste_expire_date).strip().upper()
+ argv['api_paste_expire_date'] = paste_expire_date
+
+ # lets try to read the URL that we've just built.
+ request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv))
+ response = request_string.read()
+
+ # do some basic error checking here so we can gracefully handle any
+ # errors we are likely to encounter
+ if response.startswith(self._bad_request):
+ raise PastebinError(response)
+ elif not response.startswith(self._prefix_url):
+ raise PastebinError(response)
+
+ return response
+
+ def legacy_paste(self, paste_code,
+ paste_name=None, paste_private=None,
+ paste_expire_date=None, paste_format=None):
+ """Unofficial python interface to the Pastebin legacy API.
+
+ Unlike the official API, this one doesn't require an API key, so it's
+ virtually anonymous.
+
+
+ Usage Example::
+ from pastebin import PastebinAPI
+ x = PastebinAPI()
+ url = x.legacy_paste('Snippet of code to paste goes here',
+ paste_name = 'title of paste',
+ paste_private = 'unlisted',
+ paste_expire_date = '10M',
+ paste_format = 'python')
+ print url
+ http://pastebin.com/tawPUgqY
+
+
+ @type paste_code: string
+ @param paste_code: The file or string to paste to body of the
+ U{http://pastebin.com} paste.
+
+ @type paste_name: string
+ @param paste_name: (Optional) Title of the paste.
+ Default is to paste with no title.
+
+ @type paste_private: string
+ @param paste_private: (Optional) C{'public'} if the paste is public
+ (visible by everyone), C{'unlisted'} if it's public but not
+ searchable. C{'private'} if the paste is private and not
+ searchable or indexed.
+ The Pastebin FAQ (U{http://pastebin.com/faq}) claims
+ private pastes are not indexed by search engines (aka Google).
+
+ @type paste_expire_date: string
+ @param paste_expire_date: (Optional) Expiration date for the paste.
+ Once past this date the paste is deleted automatically. Valid
+ values are found in the L{PastebinAPI.paste_expire_date} class
+ member.
+ If not provided, the paste never expires.
+
+ @type paste_format: string
+ @param paste_format: (Optional) Programming language of the code being
+ pasted. This enables syntax highlighting when reading the code in
+ U{http://pastebin.com}. Default is no syntax highlighting (text is
+ just text and not source code).
+
+ @rtype: string
+ @return: Returns the URL to the newly created paste.
+ """
+
+ # Code snippet to submit
+ argv = {'paste_code': str(paste_code)}
+
+ # Name of the poster
+ if paste_name is not None:
+ argv['paste_name'] = str(paste_name)
+
+ # Is the snippet private?
+ if paste_private is not None:
+ argv['paste_private'] = int(bool(int(paste_private)))
+
+ # Expiration for the snippet
+ if paste_expire_date is not None:
+ paste_expire_date = str(paste_expire_date).strip().upper()
+ argv['paste_expire_date'] = paste_expire_date
+
+ # Syntax highlighting
+ if paste_format is not None:
+ paste_format = str(paste_format).strip().lower()
+ argv['paste_format'] = paste_format
+
+ # lets try to read the URL that we've just built.
+ data = urllib.urlencode(argv)
+ request_string = urllib.urlopen(self._legacy_api_url, data)
+ response = request_string.read()
+
+ # do some basic error checking here so we can gracefully handle any
+ # errors we are likely to encounter
+ if response.startswith(self._bad_request):
+ raise PastebinError(response)
+ elif not response.startswith(self._prefix_url):
+ raise PastebinError(response)
+
+ return response
+
+
+######################################################
+
+delete_paste = PastebinAPI.delete_paste
+user_details = PastebinAPI.user_details
+trending = PastebinAPI.trending
+pastes_by_user = PastebinAPI.pastes_by_user
+generate_user_key = PastebinAPI.generate_user_key
+legacy_paste = PastebinAPI.legacy_paste
+paste = PastebinAPI.paste