#!/usr/bin/python # Copyright (C) 2011 Canonical # # Authors: # Michael Vogt # # 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; version 3. # # 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, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import httplib2 import argparse import logging import os import json import pickle import sys # useful for debugging if "SOFTWARE_CENTER_DEBUG_HTTP" in os.environ: httplib2.debuglevel = 1 import piston_mini_client.auth import piston_mini_client.failhandlers from piston_mini_client.failhandlers import APIError try: import softwarecenter except ImportError: if os.path.exists("../softwarecenter"): sys.path.insert(0, "../") else: sys.path.insert(0, "/usr/share/software-center") import softwarecenter.paths from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR from softwarecenter.backend.ubuntusso import UbuntuSSO # the piston import from softwarecenter.backend.piston.ubuntusso_pristine import UbuntuSsoAPI from softwarecenter.backend.piston.rnrclient import RatingsAndReviewsAPI from softwarecenter.backend.piston.scaclient import SoftwareCenterAgentAPI from softwarecenter.backend.piston.sreclient_pristine import ( SoftwareCenterRecommenderAPI) from softwarecenter.enums import RECOMMENDER_HOST SoftwareCenterRecommenderAPI.default_service_root = \ RECOMMENDER_HOST + "/api/1.0" # patch default_service_root to the one we use from softwarecenter.enums import UBUNTU_SSO_SERVICE # *Don't* append /api/1.0, as it's already included in UBUNTU_SSO_SERVICE UbuntuSsoAPI.default_service_root = UBUNTU_SSO_SERVICE RatingsAndReviewsAPI # pyflakes UbuntuSsoAPI # pyflakes SoftwareCenterAgentAPI # pyflakes SoftwareCenterRecommenderAPI # pyflakes LOG = logging.getLogger(__name__) if __name__ == "__main__": logging.basicConfig() # command line parser parser = argparse.ArgumentParser( description="Backend helper for piston-mini-client based APIs") parser.add_argument("--debug", action="store_true", default=False, help="enable debug output") parser.add_argument("--datadir", default="/usr/share/software-center", help="setup alternative datadir") parser.add_argument("--ignore-cache", action="store_true", default=False, help="force ignore cache") parser.add_argument("--disable-offline-mode", action="store_true", default=False, help="force disable offline mode") parser.add_argument("--needs-auth", default=False, action="store_true", help="need oauth credentials") parser.add_argument("--no-relogin", default=False, action="store_true", help="do not attempt relogin if token is invalid") parser.add_argument("--output", default="pickle", help="output result as [pickle|json|text]") parser.add_argument("--parent-xid", default=0, help="xid of the parent window") parser.add_argument('klass', help='class to use') parser.add_argument('function', help='function to call') parser.add_argument('kwargs', nargs="?", help='kwargs for the function call as json') args = parser.parse_args() if args.debug: logging.basicConfig(level=logging.DEBUG) LOG.setLevel(logging.DEBUG) if args.ignore_cache: cachedir = None else: cachedir = os.path.join(SOFTWARE_CENTER_CACHE_DIR, "piston-helper") # check what we need to call klass = globals()[args.klass] func = args.function kwargs = json.loads(args.kwargs or '{}') softwarecenter.paths.datadir = args.datadir if args.needs_auth: helper = UbuntuSSO(args.parent_xid) token = helper.get_oauth_token_and_verify_sync( no_relogin=args.no_relogin) # if we don't have a token, error here if not token: # it may happen that the parent is closed already so the pipe # is gone, that is ok as we exit anyway try: sys.stderr.write("ERROR: can not obtain a oauth token\n") except IOError: pass sys.exit(1) auth = piston_mini_client.auth.OAuthAuthorizer( token["token"], token["token_secret"], token["consumer_key"], token["consumer_secret"]) api = klass(cachedir=cachedir, auth=auth) else: api = klass(cachedir=cachedir) piston_reply = None # handle the args f = getattr(api, func) try: piston_reply = f(**kwargs) except httplib2.ServerNotFoundError as e: if not args.disable_offline_mode: # switch to offline mode and try again from the cache try: api._offline_mode = True piston_reply = f(**kwargs) except Exception as e: LOG.warn(e) sys.exit(1) except APIError as e: LOG.warn(e) sys.exit(1) except: LOG.exception("urclient_apps") sys.exit(1) # no data is a error, the server does always return something, # this can happen if e.g. we try cached data but have nothing # in the cache if piston_reply is None: LOG.warn("no data") sys.exit(1) if args.debug: for itm in piston_reply: s = "** itm: %s\n" % itm for var in vars(itm): s += "%s: '%s'\n" % (var, getattr(itm, var)) LOG.debug(s) # print to stdout where its consumed by the parent # check what format to use if args.output == "pickle": res = pickle.dumps(piston_reply) elif args.output == "json": res = json.dumps(piston_reply) elif args.output == "text": res = piston_reply # and output it try: print res except IOError: # this can happen if the parent gets killed, no need to trigger # apport for this pass