Updated debdiff attached (deleted file detail again removed): Changes
- Version fixed - systemd implementation reverted to init script - fixed self reference in cps-auth(1) - use {Python:Depends} - systemd dependency removed - revert to the stable version of python-daemon https://github.com/davesteele/cloudprint-service/compare/debian/0.13-1...updates-jessie -- "Le mieux est l'ennemi du bien" - Voltaire
diff -Nru cloudprint-0.11/cloudprint/cloudprint.py cloudprint-0.13/cloudprint/cloudprint.py --- cloudprint-0.11/cloudprint/cloudprint.py 2014-01-05 19:29:25.000000000 -0500 +++ cloudprint-0.13/cloudprint/cloudprint.py 2015-07-08 23:10:28.000000000 -0400 @@ -1,231 +1,264 @@ #!/usr/bin/env python -import rest -import platform +# Copyright 2014 Jason Michalski <arm...@armooo.net> +# +# This file is part of cloudprint. +# +# cloudprint 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. +# +# cloudprint 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 cloudprint. If not, see <http://www.gnu.org/licenses/>. + +import argparse import cups +import datetime import hashlib -import time -import urllib2 -import tempfile -import shutil -import os import json -import getpass -import stat -import sys -import getopt import logging import logging.handlers +import os +import re +import requests +import shutil +import stat +import sys +import tempfile +import time +import uuid import xmpp XMPP_SERVER_HOST = 'talk.google.com' -XMPP_USE_SSL = True XMPP_SERVER_PORT = 5223 SOURCE = 'Armooo-PrintProxy-1' PRINT_CLOUD_SERVICE_ID = 'cloudprint' CLIENT_LOGIN_URL = '/accounts/ClientLogin' -PRINT_CLOUD_URL = '/cloudprint/' +PRINT_CLOUD_URL = 'https://www.google.com/cloudprint/' # period in seconds with which we should poll for new jobs via the HTTP api, # when xmpp is connecting properly. # 'None' to poll only on startup and when we get XMPP notifications. # 'Fast Poll' is used as a workaround when notifications are not working. -POLL_PERIOD=3600.0 -FAST_POLL_PERIOD=30.0 +POLL_PERIOD = 3600.0 +FAST_POLL_PERIOD = 30.0 # wait period to retry when xmpp fails -FAIL_RETRY=60 +FAIL_RETRY = 60 # how often, in seconds, to send a keepalive character over xmpp -KEEPALIVE=600.0 +KEEPALIVE = 600.0 + +# failed job retries +RETRIES = 1 +num_retries = 0 LOGGER = logging.getLogger('cloudprint') LOGGER.setLevel(logging.INFO) -class CloudPrintProxy(object): +CLIENT_ID = '607830223128-rqenc3ekjln2qi4m4ntudskhnsqn82gn.apps.googleusercontent.com' +CLIENT_KEY = 'T0azsx2lqDztSRyPHQaERJJH' - def __init__(self, verbose=True): - self.verbose = verbose - self.auth = None - self.cups= cups.Connection() - self.proxy = platform.node() + '-Armooo-PrintProxy' - self.auth_path = os.path.expanduser('~/.cloudprintauth') - self.xmpp_auth_path = os.path.expanduser('~/.cloudprintauth.sasl') - self.username = None - self.password = None - self.sleeptime = 0 - def get_auth(self): - if self.auth: - return self.auth - if not self.auth: - auth = self.get_saved_auth() - if auth: - return auth - - r = rest.REST('https://www.google.com', debug=False) - try: - auth_response = r.post( - CLIENT_LOGIN_URL, - { - 'accountType': 'GOOGLE', - 'Email': self.username, - 'Passwd': self.password, - 'service': PRINT_CLOUD_SERVICE_ID, - 'source': SOURCE, - }, - 'application/x-www-form-urlencoded') - xmpp_response = r.post(CLIENT_LOGIN_URL, - { - 'accountType': 'GOOGLE', - 'Email': self.username, - 'Passwd': self.password, - 'service': 'mail', - 'source': SOURCE, - }, - 'application/x-www-form-urlencoded') - jid = self.username if '@' in self.username else self.username + '@gmail.com' - sasl_token = ('\0%s\0%s' % (jid, xmpp_response['Auth'])).encode('base64') - file(self.xmpp_auth_path, 'w').write(sasl_token) - except rest.REST.RESTException, e: - if 'InvalidSecondFactor' in e.msg: - raise rest.REST.RESTException( - '2-Step', - '403', - 'You have 2-Step authentication enabled on your ' - 'account. \n\nPlease visit ' - 'https://www.google.com/accounts/IssuedAuthSubTokens ' - 'to generate an application-specific password.' - ) - else: - raise +class CloudPrintAuth(object): + def __init__(self, auth_path): + self.auth_path = auth_path + self.guid = None + self.email = None + self.xmpp_jid = None + self.exp_time = None + self.refresh_token = None + self._access_token = None + + @property + def session(self): + s = requests.session() + s.headers['X-CloudPrint-Proxy'] = 'ArmoooIsAnOEM' + s.headers['Authorization'] = 'Bearer {0}'.format(self.access_token) + return s + + @property + def access_token(self): + if datetime.datetime.now() > self.exp_time: + self.refresh() + return self._access_token + + def no_auth(self): + return not os.path.exists(self.auth_path) + + def login(self, name, description, ppd): + self.guid = str(uuid.uuid4()) + reg_data = requests.post( + PRINT_CLOUD_URL + 'register', + { + 'output': 'json', + 'printer': name, + 'proxy': self.guid, + 'capabilities': ppd.encode('utf-8'), + 'defaults': ppd.encode('utf-8'), + 'status': 'OK', + 'description': description, + 'capsHash': hashlib.sha1(ppd.encode('utf-8')).hexdigest(), + }, + headers={'X-CloudPrint-Proxy': 'ArmoooIsAnOEM'}, + ).json() + print 'Goto {0} to clame this printer'.format(reg_data['complete_invite_url']) + + end = time.time() + int(reg_data['token_duration']) + while time.time() < end: + time.sleep(10) + print 'trying for the win' + poll = requests.get( + reg_data['polling_url'] + CLIENT_ID, + headers={'X-CloudPrint-Proxy': 'ArmoooIsAnOEM'}, + ).json() + if poll['success']: + break + else: + print 'The login request timedout' - self.set_auth(auth_response['Auth']) - return self.auth + self.xmpp_jid = poll['xmpp_jid'] + self.email = poll['user_email'] + print 'Printer claimed by {0}.'.format(self.email) + + token = requests.post( + 'https://accounts.google.com/o/oauth2/token', + data={ + 'redirect_uri': 'oob', + 'client_id': CLIENT_ID, + 'client_secret': CLIENT_KEY, + 'grant_type': 'authorization_code', + 'code': poll['authorization_code'], + } + ).json() + + self.refresh_token = token['refresh_token'] + self.refresh() + + self.save() + + def refresh(self): + token = requests.post( + 'https://accounts.google.com/o/oauth2/token', + data={ + 'client_id': CLIENT_ID, + 'client_secret': CLIENT_KEY, + 'grant_type': 'refresh_token', + 'refresh_token': self.refresh_token, + } + ).json() + self._access_token = token['access_token'] + + slop_time = datetime.timedelta(minutes=15) + expires_in = datetime.timedelta(seconds=token['expires_in']) + self.exp_time = datetime.datetime.now() + (expires_in - slop_time) - def get_saved_auth(self): + def load(self): if os.path.exists(self.auth_path): - auth_file = open(self.auth_path) - self.auth = auth_file.read() - auth_file.close() - return self.auth + with open(self.auth_path) as auth_file: + auth_data = json.load(auth_file) + self.guid = auth_data['guid'] + self.xmpp_jid = auth_data['xmpp_jid'] + self.email = auth_data['email'] + self.refresh_token = auth_data['refresh_token'] + + self.refresh() - def del_saved_auth(self): + def delete(self): if os.path.exists(self.auth_path): os.unlink(self.auth_path) - def set_auth(self, auth): - self.auth = auth + def save(self): if not os.path.exists(self.auth_path): - auth_file = open(self.auth_path, 'w') - os.chmod(self.auth_path, stat.S_IRUSR | stat.S_IWUSR) - auth_file.close() - auth_file = open(self.auth_path, 'w') - auth_file.write(self.auth) - auth_file.close() - - def get_rest(self): - class check_new_auth(object): - def __init__(self, rest): - self.rest = rest - - def __getattr__(in_self, key): - attr = getattr(in_self.rest, key) - if not attr: - raise AttributeError() - if not hasattr(attr, '__call__'): - return attr - - def f(*arg, **karg): - r = attr(*arg, **karg) - if 'update-client-auth' in r.headers: - self.set_auth(r.headers['update-client-auth']) - return r - return f + with open(self.auth_path, 'w') as auth_file: + os.chmod(self.auth_path, stat.S_IRUSR | stat.S_IWUSR) + with open(self.auth_path, 'w') as auth_file: + json.dump({ + 'guid': self.guid, + 'email': self.email, + 'xmpp_jid': self.xmpp_jid, + 'refresh_token': self.refresh_token, + }, + auth_file + ) + - auth = self.get_auth() - return check_new_auth(rest.REST('https://www.google.com', auth=auth, debug=False)) +class CloudPrintProxy(object): + + def __init__(self, auth): + self.auth = auth + self.sleeptime = 0 + self.include = [] + self.exclude = [] def get_printers(self): - r = self.get_rest() - printers = r.post( + printers = self.auth.session.post( PRINT_CLOUD_URL + 'list', { 'output': 'json', - 'proxy': self.proxy, + 'proxy': self.auth.guid, }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM'}, - ) - return [ PrinterProxy(self, p['id'], p['name']) for p in printers['printers'] ] + ).json() + return [PrinterProxy(self, p['id'], p['name']) for p in printers['printers']] def delete_printer(self, printer_id): - r = self.get_rest() - docs = r.post( + self.auth.session.post( PRINT_CLOUD_URL + 'delete', { - 'output' : 'json', + 'output': 'json', 'printerid': printer_id, - }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM'}, - ) - if self.verbose: - LOGGER.info('Deleted printer '+ printer_id) + }, + ).raise_for_status() + LOGGER.debug('Deleted printer ' + printer_id) def add_printer(self, name, description, ppd): - r = self.get_rest() - r.post( + self.auth.session.post( PRINT_CLOUD_URL + 'register', { - 'output' : 'json', - 'printer' : name, - 'proxy' : self.proxy, - 'capabilities' : ppd.encode('utf-8'), - 'defaults' : ppd.encode('utf-8'), - 'status' : 'OK', - 'description' : description, - 'capsHash' : hashlib.sha1(ppd.encode('utf-8')).hexdigest(), - }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM'}, - ) - if self.verbose: - LOGGER.info('Added Printer ' + name) + 'output': 'json', + 'printer': name, + 'proxy': self.auth.guid, + 'capabilities': ppd.encode('utf-8'), + 'defaults': ppd.encode('utf-8'), + 'status': 'OK', + 'description': description, + 'capsHash': hashlib.sha1(ppd.encode('utf-8')).hexdigest(), + }, + ).raise_for_status() + LOGGER.debug('Added Printer ' + name) def update_printer(self, printer_id, name, description, ppd): - r = self.get_rest() - r.post( + self.auth.session.post( PRINT_CLOUD_URL + 'update', { - 'output' : 'json', - 'printerid' : printer_id, - 'printer' : name, - 'proxy' : self.proxy, - 'capabilities' : ppd.encode('utf-8'), - 'defaults' : ppd.encode('utf-8'), - 'status' : 'OK', - 'description' : description, - 'capsHash' : hashlib.sha1(ppd.encode('utf-8')).hexdigest(), - }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM'}, - ) - if self.verbose: - LOGGER.info('Updated Printer ' + name) + 'output': 'json', + 'printerid': printer_id, + 'printer': name, + 'proxy': self.auth.guid, + 'capabilities': ppd.encode('utf-8'), + 'defaults': ppd.encode('utf-8'), + 'status': 'OK', + 'description': description, + 'capsHash': hashlib.sha1(ppd.encode('utf-8')).hexdigest(), + }, + ).raise_for_status() + LOGGER.debug('Updated Printer ' + name) def get_jobs(self, printer_id): - r = self.get_rest() - docs = r.post( + docs = self.auth.session.post( PRINT_CLOUD_URL + 'fetch', { - 'output' : 'json', + 'output': 'json', 'printerid': printer_id, - }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM'}, - ) + }, + ).json() if not 'jobs' in docs: return [] @@ -233,30 +266,27 @@ return docs['jobs'] def finish_job(self, job_id): - r = self.get_rest() - r.post( + self.auth.session.post( PRINT_CLOUD_URL + 'control', { - 'output' : 'json', + 'output': 'json', 'jobid': job_id, 'status': 'DONE', - }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM' }, - ) + }, + ).json() + LOGGER.debug('Finished Job' + job_id) def fail_job(self, job_id): - r = self.get_rest() - r.post( + self.auth.session.post( PRINT_CLOUD_URL + 'control', { - 'output' : 'json', + 'output': 'json', 'jobid': job_id, 'status': 'ERROR', - }, - 'application/x-www-form-urlencoded', - { 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM' }, - ) + }, + ).json() + LOGGER.debug('Failed Job' + job_id) + class PrinterProxy(object): def __init__(self, cpp, printer_id, name): @@ -274,19 +304,27 @@ def delete(self): return self.cpp.delete_printer(self.id) -class App(object): - def __init__(self, cups_connection=None, cpp=None, printers=None, pidfile_path=None): - self.cups_connection = cups_connection - self.cpp = cpp - self.printers = printers - self.pidfile_path = pidfile_path - self.stdin_path = '/dev/null' - self.stdout_path = '/dev/null' - self.stderr_path = '/dev/null' - self.pidfile_timeout = 5 - def run(self): - process_jobs(self.cups_connection, self.cpp, self.printers) +#True if printer name matches *any* of the regular expressions in regexps +def match_re(prn, regexps, empty=False): + if len(regexps): + try: + return re.match(regexps[0], prn, re.UNICODE) or match_re(prn, regexps[1:]) + except Exception: + sys.stderr.write('cloudprint: invalid regular expression: ' + regexps[0] + '\n') + sys.exit(1) + else: + return empty + + +def get_printer_info(cups_connection, printer_name): + with open(cups_connection.getPPD(printer_name)) as ppd_file: + ppd = ppd_file.read() + #This is bad it should use the LanguageEncoding in the PPD + #But a lot of utf-8 PPDs seem to say they are ISOLatin1 + ppd = ppd.decode('utf-8') + description = cups_connection.getPrinterAttributes(printer_name)['printer-info'] + return ppd, description def sync_printers(cups_connection, cpp): @@ -294,210 +332,173 @@ remote_printers = dict([(p.name, p) for p in cpp.get_printers()]) remote_printer_names = set(remote_printers) + #Include/exclude local printers + local_printer_names = set([prn for prn in local_printer_names if match_re(prn, cpp.include, True)]) + local_printer_names = set([prn for prn in local_printer_names if not match_re(prn, cpp.exclude)]) + #New printers for printer_name in local_printer_names - remote_printer_names: try: - ppd_file = open(cups_connection.getPPD(printer_name)) - ppd = ppd_file.read() - ppd_file.close() - #This is bad it should use the LanguageEncoding in the PPD - #But a lot of utf-8 PPDs seem to say they are ISOLatin1 - ppd = ppd.decode('utf-8') - description = cups_connection.getPrinterAttributes(printer_name)['printer-info'] + ppd, description = get_printer_info(cups_connection, printer_name) cpp.add_printer(printer_name, description, ppd) except (cups.IPPError, UnicodeDecodeError): LOGGER.info('Skipping ' + printer_name) #Existing printers for printer_name in local_printer_names & remote_printer_names: - ppd_file = open(cups_connection.getPPD(printer_name)) - ppd = ppd_file.read() - ppd_file.close() - #This is bad it should use the LanguageEncoding in the PPD - #But a lot of utf-8 PPDs seem to say they are ISOLatin1 - try: - ppd = ppd.decode('utf-8') - except UnicodeDecodeError: - pass - description = cups_connection.getPrinterAttributes(printer_name)['printer-info'] + ppd, description = get_printer_info(cups_connection, printer_name) remote_printers[printer_name].update(description, ppd) #Printers that have left us for printer_name in remote_printer_names - local_printer_names: remote_printers[printer_name].delete() + def process_job(cups_connection, cpp, printer, job): - request = urllib2.Request(job['fileUrl'], headers={ - 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM', - 'Authorization' : 'GoogleLogin auth=%s' % cpp.get_auth() - }) + global num_retries try: - pdf = urllib2.urlopen(request) + pdf = cpp.auth.session.get(job['fileUrl'], stream=True) tmp = tempfile.NamedTemporaryFile(delete=False) - shutil.copyfileobj(pdf, tmp) + shutil.copyfileobj(pdf.raw, tmp) tmp.flush() - request = urllib2.Request(job['ticketUrl'], headers={ - 'X-CloudPrint-Proxy' : 'ArmoooIsAnOEM', - 'Authorization' : 'GoogleLogin auth=%s' % cpp.get_auth() - }) - options = json.loads(urllib2.urlopen(request).read()) - if 'request' in options: del options['request'] - options = dict( (str(k), str(v)) for k, v in options.items() ) + options = cpp.auth.session.get(job['ticketUrl']).json() + if 'request' in options: + del options['request'] - cpp.finish_job(job['id']) + options = dict((str(k), str(v)) for k, v in options.items()) - cups_connection.printFile(printer.name, tmp.name, job['title'], options) + # Cap the title length to 255, or cups will complain about invalid job-name + cups_connection.printFile(printer.name, tmp.name, job['title'][:255], options) os.unlink(tmp.name) LOGGER.info('SUCCESS ' + job['title'].encode('unicode-escape')) - except: - cpp.fail_job(job['id']) - LOGGER.error('ERROR ' + job['title'].encode('unicode-escape')) + cpp.finish_job(job['id']) + num_retries = 0 + + except Exception: + if num_retries >= RETRIES: + num_retries = 0 + cpp.fail_job(job['id']) + LOGGER.error('ERROR ' + job['title'].encode('unicode-escape')) + else: + num_retries += 1 + LOGGER.info('Job %s failed - Will retry' % job['title'].encode('unicode-escape')) -def process_jobs(cups_connection, cpp, printers): - xmpp_auth = file(cpp.xmpp_auth_path).read() + +def process_jobs(cups_connection, cpp): xmpp_conn = xmpp.XmppConnection(keepalive_period=KEEPALIVE) while True: + printers = cpp.get_printers() try: for printer in printers: for job in printer.get_jobs(): process_job(cups_connection, cpp, printer, job) if not xmpp_conn.is_connected(): - xmpp_conn.connect(XMPP_SERVER_HOST,XMPP_SERVER_PORT, - XMPP_USE_SSL,xmpp_auth) + xmpp_conn.connect(XMPP_SERVER_HOST, XMPP_SERVER_PORT, cpp.auth) xmpp_conn.await_notification(cpp.sleeptime) - except: + except Exception: global FAIL_RETRY - LOGGER.error('ERROR: Could not Connect to Cloud Service. Will Try again in %d Seconds' % FAIL_RETRY) + LOGGER.exception('ERROR: Could not Connect to Cloud Service. Will Try again in %d Seconds' % FAIL_RETRY) time.sleep(FAIL_RETRY) -def usage(): - print sys.argv[0] + ' [-d][-l][-h][-c][-f][-v] [-p pid_file] [-a account_file]' - print '-d\t\t: enable daemon mode (requires the daemon module)' - print '-l\t\t: logout of the google account' - print '-p pid_file\t: path to write the pid to (default cloudprint.pid)' - print '-a account_file\t: path to google account ident data (default ~/.cloudprintauth)' - print '\t\t account_file format:\t <Google username>' - print '\t\t\t\t\t <Google password>' - print '-c\t\t: establish and store login credentials, then exit' - print '-f\t\t: use fast poll if notifications are not working' - print '-v\t\t: verbose logging' - print '-h\t\t: display this help' - def main(): - opts, args = getopt.getopt(sys.argv[1:], 'dlhp:a:cvf') - daemon = False - logout = False - pidfile = None - authfile = None - authonly = False - verbose = False - saslauthfile = None - fastpoll = False - for o, a in opts: - if o == '-d': - daemon = True - elif o == '-l': - logout = True - elif o == '-p': - pidfile = a - elif o == '-a': - authfile = a - saslauthfile = authfile+'.sasl' - elif o == '-c': - authonly = True - elif o == '-v': - verbose = True - elif o == '-f': - fastpoll = True - elif o =='-h': - usage() - sys.exit() - if not pidfile: - pidfile = 'cloudprint.pid' + parser = argparse.ArgumentParser() + parser.add_argument('-d', dest='daemon', action='store_true', + help='enable daemon mode (requires the daemon module)') + parser.add_argument('-l', dest='logout', action='store_true', + help='logout of the google account') + parser.add_argument('-p', metavar='pid_file', dest='pidfile', default='cloudprint.pid', + help='path to write the pid to (default %(default)s)') + parser.add_argument('-a', metavar='account_file', dest='authfile', default=os.path.expanduser('~/.cloudprintauth.json'), + help='path to google account ident data (default %(default)s)') + parser.add_argument('-c', dest='authonly', action='store_true', + help='establish and store login credentials, then exit') + parser.add_argument('-f', dest='fastpoll', action='store_true', + help='use fast poll if notifications are not working') + parser.add_argument('-i', metavar='regexp', dest='include', default=[], action='append', + help='include local printers matching %(metavar)s') + parser.add_argument('-x', metavar='regexp', dest='exclude', default=[], action='append', + help='exclude local printers matching %(metavar)s') + parser.add_argument('-v', dest='verbose', action='store_true', + help='verbose logging') + args = parser.parse_args() # if daemon, log to syslog, otherwise log to stdout - if daemon: - handler = logging.handlers.SysLogHandler(address='/dev/log') + if args.daemon: + handler = logging.handlers.SysLogHandler() handler.setFormatter(logging.Formatter(fmt='cloudprint.py: %(message)s')) else: handler = logging.StreamHandler(sys.stdout) LOGGER.addHandler(handler) - if verbose: + if args.verbose: LOGGER.info('Setting DEBUG-level logging') LOGGER.setLevel(logging.DEBUG) + import httplib + httplib.HTTPConnection.debuglevel = 1 + requests_log = logging.getLogger("requests.packages.urllib3") + requests_log.setLevel(logging.DEBUG) + requests_log.propagate = True + + auth = CloudPrintAuth(args.authfile) + if args.logout: + auth.delete() + LOGGER.info('logged out') + return + cups_connection = cups.Connection() - cpp = CloudPrintProxy() - if authfile: - cpp.auth_path = authfile - cpp.xmpp_auth_path = saslauthfile + cpp = CloudPrintProxy(auth) cpp.sleeptime = POLL_PERIOD - if fastpoll: + if args.fastpoll: cpp.sleeptime = FAST_POLL_PERIOD - if logout: - cpp.del_saved_auth() - LOGGER.info('logged out') + cpp.include = args.include + cpp.exclude = args.exclude + + printers = cups_connection.getPrinters().keys() + if not printers: + LOGGER.error('No printers found') return - # Check if authentification is needed - if not cpp.get_saved_auth(): - if authfile and os.path.exists(authfile): - account_file = open(authfile) - cpp.username = account_file.next().rstrip() - cpp.password = account_file.next().rstrip() - account_file.close() + if auth.no_auth(): + name = printers[0] + ppd, description = get_printer_info(cups_connection, name) + auth.login(name, description, ppd) + else: + auth.load() - else: - cpp.username = raw_input('Google username: ') - cpp.password = getpass.getpass() + sync_printers(cups_connection, cpp) - #try to login - while True: - try: - sync_printers(cups_connection, cpp) - break - except rest.REST.RESTException, e: - #not a auth error - if e.code != 403: - raise - #don't have a stored auth key - if not cpp.get_saved_auth(): - raise - #reset the stored auth - cpp.set_auth('') - - if authonly: + if args.authonly: sys.exit(0) - printers = cpp.get_printers() - - if daemon: + if args.daemon: try: - from daemon import runner + import daemon + import daemon.pidfile except ImportError: print 'daemon module required for -d' - print '\tyum install python-daemon, or apt-get install python-daemon, or pip install python-daemon' + print '\tyum install python-daemon, or apt-get install python-daemon, or pip install cloudprint[daemon]' sys.exit(1) - - app = App(cups_connection=cups_connection, - cpp=cpp, printers=printers, - pidfile_path=os.path.abspath(pidfile)) - sys.argv=[sys.argv[0], 'start'] - daemon_runner = runner.DaemonRunner(app) - daemon_runner.do_action() + + pidfile = daemon.pidfile.TimeoutPIDLockFile( + path=os.path.abspath(args.pidfile), + timeout=5, + ) + with daemon.DaemonContext(pidfile=pidfile): + process_jobs(cups_connection, cpp) + else: - process_jobs(cups_connection, cpp, printers) + process_jobs(cups_connection, cpp) if __name__ == '__main__': main() diff -Nru cloudprint-0.11/cloudprint/__init__.py cloudprint-0.13/cloudprint/__init__.py --- cloudprint-0.11/cloudprint/__init__.py 2012-09-13 19:08:21.000000000 -0400 +++ cloudprint-0.13/cloudprint/__init__.py 2015-06-08 00:57:22.000000000 -0400 @@ -0,0 +1,16 @@ +# Copyright 2014 Jason Michalski <arm...@armooo.net> +# +# This file is part of cloudprint. +# +# cloudprint 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. +# +# cloudprint 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 cloudprint. If not, see <http://www.gnu.org/licenses/>. diff -Nru cloudprint-0.11/cloudprint/xmpp.py cloudprint-0.13/cloudprint/xmpp.py --- cloudprint-0.11/cloudprint/xmpp.py 2014-01-05 19:29:25.000000000 -0500 +++ cloudprint-0.13/cloudprint/xmpp.py 2015-06-08 00:57:22.000000000 -0400 @@ -1,3 +1,19 @@ +# Copyright 2014 Jason Michalski <arm...@armooo.net> +# This file is part of cloudprint. +# +# cloudprint 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. +# +# cloudprint 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 + +import base64 import logging import ssl import socket @@ -9,8 +25,9 @@ LOGGER = logging.getLogger('cloudprint.xmpp') + class XmppXmlHandler(object): - STREAM_TAG='{http://etherx.jabber.org/streams}stream' + STREAM_TAG = '{http://etherx.jabber.org/streams}stream' def __init__(self): self._stack = 0 @@ -48,6 +65,7 @@ except IndexError: return None + class XmppConnection(object): def __init__(self, keepalive_period=60.0): self._connected = False @@ -96,7 +114,6 @@ # need more data; block until it becomes available self._read_socket() - def _check_for_notification(self): """Check for any notifications which have already been received""" return(self._handler.get_elem() is not None) @@ -105,8 +122,7 @@ LOGGER.info("Sending XMPP keepalive") self._write_socket(" ") - - def connect(self, host, port, use_ssl, sasl_token): + def connect(self, host, port, auth): """Establish a new connection to the XMPP server""" # first close any existing socket self.close() @@ -115,10 +131,10 @@ (host, port)) self._xmppsock = socket.socket() self._wrappedsock = self._xmppsock + auth_string = base64.b64encode('\0{0}\0{1}'.format(auth.xmpp_jid, auth.access_token)) try: - if use_ssl: - self._wrappedsock = ssl.wrap_socket(self._xmppsock) + self._wrappedsock = ssl.wrap_socket(self._xmppsock) self._wrappedsock.connect((host, port)) self._handler = XmppXmlHandler() @@ -126,9 +142,9 @@ # https://developers.google.com/cloud-print/docs/rawxmpp self._msg('<stream:stream to="gmail.com" xml:lang="en" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">') - self._msg('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-GOOGLE-TOKEN" auth:allow-generated-jid="true" auth:client-uses-full-bind-result="true" xmlns:auth="http://www.google.com/talk/protocol/auth">%s</auth>' % sasl_token) + self._msg('<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-OAUTH2">%s</auth>' % auth_string) self._msg('<stream:stream to="gmail.com" xml:lang="en" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">') - iq = self._msg('<iq type="set" id="0"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>Armooo</resource></bind></iq>') + iq = self._msg('<iq type="set" id="0"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>cloud_print</resource></bind></iq>') bare_jid = iq[0][0].text.split('/')[0] self._msg('<iq type="set" id="2"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>') self._msg('<iq type="set" id="3" to="%s"><subscribe xmlns="google:push"><item channel="cloudprint.google.com" from="cloudprint.google.com"/></subscribe></iq>' % bare_jid) @@ -139,23 +155,24 @@ LOGGER.info("xmpp connection established") self._connected = True - def close(self): """Close the connection to the XMPP server""" - if self._wrappedsock is not None: + try: self._wrappedsock.shutdown(socket.SHUT_RDWR) self._wrappedsock.close() + except: + # close() is best effort. Don't respond to failures + LOGGER.debug("Error encountered closing XMPP socket") + finally: + self._connected = False + self._nextkeepalive = 0 self._wrappedsock = None - self._connected = False - self._nextkeepalive = 0 - def is_connected(self): """Check if we are connected to the XMPP server returns true if the connection is active; false otherwise""" return self._connected - def await_notification(self, timeout): """wait for a timeout or event notification""" now = time.time() @@ -203,4 +220,3 @@ except: self.close() raise - diff -Nru cloudprint-0.11/cloudprint.egg-info/PKG-INFO cloudprint-0.13/cloudprint.egg-info/PKG-INFO --- cloudprint-0.11/cloudprint.egg-info/PKG-INFO 2014-01-05 19:53:29.000000000 -0500 +++ cloudprint-0.13/cloudprint.egg-info/PKG-INFO 2015-07-08 23:17:23.000000000 -0400 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cloudprint -Version: 0.11 +Version: 0.13 Summary: Google cloud print proxy for linux/OSX Home-page: https://github.com/armooo/cloudprint Author: Jason Michalski @@ -9,10 +9,14 @@ Description: Share your CUPS printers with google's cloud print. Works with linux and OS X. + This software is a python implementation of a cloud print connector. Unlike + Google's linux connector, it does not require chrome to be installed on the server. + + Requires --------------------------------------------------- - python 2.6 or 2.7 - pycups (can be tricky on OS X) + - python 2.6 or 2.7 + - pycups (can be tricky on OS X) wich depends on libcups2-dev Usage --------------------------------------------------- @@ -24,10 +28,14 @@ -l : logout of the current google account -p pid_file : path to write the pid to (default cloudprint.pid) -a account_file : path to google account ident data (optional) - account_file format: <Google username> - <Google password> -c : establish and store login credentials, then exit -f : 'fast poll', if notifications aren't working + -u : store username/password in addition to login token + to avoid authentication expiration + -i regexp : include files matching regexp + -x regexp : exclude filees matching regexp + regexp: a Python regexp, which is matched against the + start of the printer name -h : display this help Google accounts with 2 step verification enabled need to use an @@ -43,12 +51,40 @@ Password: Added Printer Brother-HL-2170W + Examples - Include/Exclude + --------------------------------------------------- + + Include only the printers "`lp`" and "`2up`": + :: + + cloudprint -i lp -i 2up + + Exclude all printers whose names start with "`GCP-`": + :: + + cloudprint -x GCP- + + By default, all printers are included. For the include and exclude options, + the argument is a regular expression which is matched against the start of the + printer name. + + For example, to include all printers whose names begin "`lp`": + :: + + cloudprint -i lp # includes both lp and lp2up + + Install --------------------------------------------------- :: pip install cloudprint + or with optional daemon support + pip install cloudprint[daemon] + + After running cloudprint, verify that the connector successfully installed the cloud printer by visiting + http://www.google.com/cloudprint/manage.html. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta diff -Nru cloudprint-0.11/cloudprint.egg-info/requires.txt cloudprint-0.13/cloudprint.egg-info/requires.txt --- cloudprint-0.11/cloudprint.egg-info/requires.txt 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/cloudprint.egg-info/requires.txt 2015-07-08 23:17:23.000000000 -0400 @@ -0,0 +1,5 @@ +requests >= 2.7.0 +pycups + +[daemon] +python-daemon >= 2.0.0 \ No newline at end of file diff -Nru cloudprint-0.11/cloudprint.egg-info/SOURCES.txt cloudprint-0.13/cloudprint.egg-info/SOURCES.txt --- cloudprint-0.11/cloudprint.egg-info/SOURCES.txt 2014-01-05 19:53:29.000000000 -0500 +++ cloudprint-0.13/cloudprint.egg-info/SOURCES.txt 2015-07-08 23:17:23.000000000 -0400 @@ -4,10 +4,10 @@ setup.py cloudprint/__init__.py cloudprint/cloudprint.py -cloudprint/rest.py cloudprint/xmpp.py cloudprint.egg-info/PKG-INFO cloudprint.egg-info/SOURCES.txt cloudprint.egg-info/dependency_links.txt cloudprint.egg-info/entry_points.txt +cloudprint.egg-info/requires.txt cloudprint.egg-info/top_level.txt \ No newline at end of file diff -Nru cloudprint-0.11/debian/changelog cloudprint-0.13/debian/changelog --- cloudprint-0.11/debian/changelog 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/changelog 2015-09-28 20:44:43.000000000 -0400 @@ -1,3 +1,72 @@ +cloudprint (0.13-1+deb8u1) stable; urgency=medium + + * Modify for Jessie + - Brings OAuth support to Jessie. + (Closes: 799227) + - Remove version requirement on python-requests. + - Revert back to support for older python-daemon module. + - Revert from systemd to init script. + + -- David Steele <dste...@gmail.com> Thu, 24 Sep 2015 21:35:16 -0400 + +cloudprint (0.13-1) unstable; urgency=low + + * Upstream update, with bug fixes + Closes: 792055 + + -- David Steele <dste...@gmail.com> Sat, 11 Jul 2015 01:09:04 -0400 + +cloudprint (0.12-3) unstable; urgency=low + + * Clear out the old init file the right way (Closes: 791624). + + -- David Steele <dste...@gmail.com> Mon, 06 Jul 2015 21:44:39 -0400 + +cloudprint (0.12-2) unstable; urgency=low + + * Don't remove the old auth file, in case the upgrade fails. + + -- David Steele <dste...@gmail.com> Mon, 06 Jul 2015 08:59:05 -0400 + +cloudprint (0.12-1) unstable; urgency=low + + * Incorporate upstream 0.12. + - Different OAuth2 implementation. + * Adapt to incompatible change to python-daemon + + -- David Steele <dste...@gmail.com> Mon, 08 Jun 2015 21:50:28 -0400 + +cloudprint (0.11-9) unstable; urgency=low + + * Remove the GNOME requirement for OAuth2. + * Remove old sysv-init files on install. + * Other minor changes. + + -- David Steele <dste...@gmail.com> Fri, 05 Jun 2015 21:25:55 -0400 + +cloudprint (0.11-8) unstable; urgency=low + + * Fixes to declared dependencies. + * Better GUI detection. + + -- David Steele <dste...@gmail.com> Wed, 03 Jun 2015 19:30:04 -0400 + +cloudprint (0.11-7) unstable; urgency=low + + * OAuth2 support, since the old SASL is not longer working. + (Closes: 787102) + * Replace sysv init with systemd. + + -- David Steele <dste...@gmail.com> Sat, 30 May 2015 16:44:05 -0400 + +cloudprint (0.11-6) unstable; urgency=low + + * Limit print jobs names to avoid CUPS fail (thanks Alexandre Bury). + * Mark the Cloud Print job as failed if CUPS fails. + * Retry the print job on failure. + + -- David Steele <dste...@gmail.com> Thu, 16 Apr 2015 21:39:22 -0400 + cloudprint (0.11-5) unstable; urgency=low * Fix init.d functionality breakage from daemon rename in 0.11-4. diff -Nru cloudprint-0.11/debian/cloudprint.man.in cloudprint-0.13/debian/cloudprint.man.in --- cloudprint-0.11/debian/cloudprint.man.in 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint.man.in 2015-09-28 20:44:43.000000000 -0400 @@ -57,15 +57,11 @@ .PP \fBPROG\fR will prompt for a username and password if the information has not been provided. -If two-factor authentication is enabled for the account, then an application-specific password must be used. .SH FILES .TP -\fI~/.cloudprintauth\fR +\fI~/.cloudprintauth.json\fR Default location for storing account authentication credentials -.TP -\fI~/.cloudprintauth.sasl\fR -Default location for storing internal authentication tokens .SH SEE ALSO .BR cloudprint-service(7), cloudprintd(8), cloudprint(1) diff -Nru cloudprint-0.11/debian/cloudprint-service.7 cloudprint-0.13/debian/cloudprint-service.7 --- cloudprint-0.11/debian/cloudprint-service.7 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint-service.7 2015-09-28 20:44:43.000000000 -0400 @@ -9,10 +9,9 @@ .SH SYNOPSIS .TP service \fBcloudprintd\fR \fIlogin\fR -Establish login credentials with Cloud Print, then immediately exit. This will -prompt for a username and password, if necessary. If the account has two-factor -authentication enabled, then an application-specific password must be used. -This and all other \fBcloudprintd\fR service commands must be run as root. +Verify that Cloud Print login credentials are established. If so, it will +immediately exit. Otherwise, it will print a URL that can be used to claim +the local printer(s), and wait for the user to complete the process. .TP service \fBcloudprintd\fR \fIstart\fR Start the \fBcloudprintd\fR service. This will fail if the credentials have not been @@ -39,10 +38,8 @@ proxy. It makes locally-defined CUPS printers available for use by approved Google users, both locally and remotely. -It is recommended that a dedicated account be used for this service. Printers -can be shared from that account to others via the printer management page at -\fIhttps://www.google.com/cloudprint?#printers\fR - +Authentication credentials can be established using the wrapper script +\fBcps-auth\fR. .SH FILES .TP \fI/etc/default/cloudprintd\fR @@ -50,4 +47,4 @@ View the file for more details. .SH SEE ALSO -.BR cloudprint(1), cloudprintd(8) +.BR cloudprint(1), cloudprintd(8), cps-auth(1) diff -Nru cloudprint-0.11/debian/cloudprint-service.cloudprintd.init cloudprint-0.13/debian/cloudprint-service.cloudprintd.init --- cloudprint-0.11/debian/cloudprint-service.cloudprintd.init 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint-service.cloudprintd.init 2015-09-28 20:44:43.000000000 -0400 @@ -37,9 +37,9 @@ INCLUDE_OPT="$INCLUDE_OPT -i $str";\ done -AUTHFILE=/var/lib/cloudprintd/authfile +AUTHFILE=/var/lib/cloudprintd/authfile.json PIDFILE=/var/run/$NAME.pid -DAEMON_ARGS="-d -u -a $AUTHFILE -p $PIDFILE $FAST_POLL $INCLUDE_OPT" +DAEMON_ARGS="-d -a $AUTHFILE -p $PIDFILE $FAST_POLL $INCLUDE_OPT" SCRIPTNAME=/etc/init.d/$DESC # Exit if the package is not installed @@ -82,9 +82,7 @@ rm -f $AUTHFILE || true rm -f $AUTHFILE.sasl || true - echo "Accounts with 2 factor authentication require an application-specific password" - - $DAEMON -c -u -a $AUTHFILE $INCLUDE_OPT + $DAEMON -c -a $AUTHFILE $INCLUDE_OPT } diff -Nru cloudprint-0.11/debian/cloudprint-service.install cloudprint-0.13/debian/cloudprint-service.install --- cloudprint-0.11/debian/cloudprint-service.install 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/cloudprint-service.install 2015-09-28 19:14:45.000000000 -0400 @@ -0,0 +1 @@ +debian/cps-auth usr/bin diff -Nru cloudprint-0.11/debian/cloudprint-service.manpages cloudprint-0.13/debian/cloudprint-service.manpages --- cloudprint-0.11/debian/cloudprint-service.manpages 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint-service.manpages 2015-09-28 19:14:45.000000000 -0400 @@ -1,2 +1,3 @@ debian/cloudprint-service.7 debian/cloudprintd.8 +debian/cps-auth.1 diff -Nru cloudprint-0.11/debian/control cloudprint-0.13/debian/control --- cloudprint-0.11/debian/control 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/control 2015-09-28 20:44:43.000000000 -0400 @@ -2,8 +2,9 @@ Maintainer: David Steele <dste...@gmail.com> Section: net Priority: optional -Build-Depends: python-setuptools (>= 0.6b3), python (>= 2.6.6-3), debhelper (>= 7.4.3) -Standards-Version: 3.9.5 +Build-Depends: python-setuptools (>= 0.6b3), python (>= 2.6.6-3), debhelper (>= 7.4.3), + dh-python +Standards-Version: 3.9.6 X-Python-Version: >= 2.7 Homepage: https://pypi.python.org/pypi/cloudprint/ Vcs-Browser: https://github.com/davesteele/cloudprint-service @@ -11,8 +12,9 @@ Package: cloudprint Architecture: all -Depends: python-cups, cups, python-daemon, ${misc:Depends}, ${python:Depends}, - python-pkg-resources, rsyslog | system-log-daemon +Depends: python-cups, cups, python-daemon (<< 2.0), ${misc:Depends}, + ${python:Depends}, python-pkg-resources, rsyslog | system-log-daemon, + python-requests Description: Google Cloud Print proxy Worker script to support a Google Cloud Print proxy. This can make locally-configured printers to be accessed by local or remote users over @@ -22,7 +24,6 @@ Architecture: all Provides: cloudprintd Depends: cloudprint, ${misc:Depends}, - initscripts (>= 2.88dsf-13.3) Description: provide a service for sharing printers on Google Cloud Print Share locally-defined CUPS printers with the Google Cloud Print service. The printers can be accessed locally or remotely by authorized users via diff -Nru cloudprint-0.11/debian/cps-auth cloudprint-0.13/debian/cps-auth --- cloudprint-0.11/debian/cps-auth 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/cps-auth 2015-09-28 19:14:45.000000000 -0400 @@ -0,0 +1,9 @@ +#!/bin/bash + +AUTHDIR=/var/lib/cloudprintd +AUTHFILE=$AUTHDIR/authfile.json + +if [ ! -w $AUTHDIR ] ; then echo "Must run as root" ; exit 1 ; fi + +rm -f $AUTHFILE +cloudprint -c -a $AUTHFILE diff -Nru cloudprint-0.11/debian/cps-auth.1 cloudprint-0.13/debian/cps-auth.1 --- cloudprint-0.11/debian/cps-auth.1 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/cps-auth.1 2015-09-28 20:44:43.000000000 -0400 @@ -0,0 +1,26 @@ +.\" Copyright 2015 David Steele <dste...@gmail.com> +.\" This file is part of cloudprint +.\" Available under the terms of the GNU General Public License version 2 or later +.TH cps-auth 1 2015-05-31 Linux "User Commands" +.SH NAME +cps-auth \- Perform OAuth2 authentication for cloudprint-service + +.SH SYNOPSIS +\fBcps-auth\fR + +.SH DESCRIPTION +The \fBcps-auth\fR provide a wrapper for entering the required OAuth2 +credentials for \fBcloudprint-service\fR. It will prompt the user with a URL +for claiming the printer, and wait for the process to complete. The +credentials are stored for \fBcloudprint-service\fR. + +The command must be run as root. + +.SH FILES +.TP +\fI/var/lib/cloudprint/authfile.json\fR +Credential storage location + +.SH SEE ALSO +.BR cloudprint-service(7), cloudprintd(8), cloudprint(1) + diff -Nru cloudprint-0.11/debian/gbp.conf cloudprint-0.13/debian/gbp.conf --- cloudprint-0.11/debian/gbp.conf 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/gbp.conf 2015-09-28 19:14:45.000000000 -0400 @@ -6,5 +6,6 @@ [git-import-orig] dch = False -[git-buildpackage] +[buildpackage] prebuild = rm README.md +postbuild = git checkout README.md cloudprint/__init__.py diff -Nru cloudprint-0.11/debian/NEWS cloudprint-0.13/debian/NEWS --- cloudprint-0.11/debian/NEWS 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/NEWS 2015-09-28 20:44:43.000000000 -0400 @@ -0,0 +1,9 @@ +cloudprint (0.13-1+deb8u1) stable; urgency=medium + + Cloudprint has been updated to use the OAuth2 authentication mechanism. + The program will attemp to log in. If it is not successful, it will + print a URL that can be used in a graphical browser to claim the printers + for a particular user account. cloudprint-service users can use the + cps-auth script to establish credentials. + + -- David Steele <dste...@gmail.com> Sun, 27 Sep 2015 17:12:28 -0400 diff -Nru cloudprint-0.11/debian/patches/0001-Change-the-name-of-the-main-script-in-the-module.patch cloudprint-0.13/debian/patches/0001-Change-the-name-of-the-main-script-in-the-module.patch --- cloudprint-0.11/debian/patches/0001-Change-the-name-of-the-main-script-in-the-module.patch 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/patches/0001-Change-the-name-of-the-main-script-in-the-module.patch 2015-09-28 20:44:43.000000000 -0400 @@ -11,15 +11,15 @@ 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py -index ce5f16d..d064d57 100644 +index b8dcb10..3403caf 100644 --- a/setup.py +++ b/setup.py -@@ -27,7 +27,7 @@ setup( +@@ -25,7 +25,7 @@ setup( packages=find_packages(exclude=['ez_setup']), - entry_points = { + entry_points={ 'console_scripts': [ - 'cloudprint = cloudprint.cloudprint:main', + 'cloudprint-cmd = cloudprint.cloudprint:main', ], }, - ) + install_requires=[ diff -Nru cloudprint-0.11/debian/patches/0002-Set-Python-path-to-load-the-right-daemon.patch cloudprint-0.13/debian/patches/0002-Set-Python-path-to-load-the-right-daemon.patch --- cloudprint-0.11/debian/patches/0002-Set-Python-path-to-load-the-right-daemon.patch 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/patches/0002-Set-Python-path-to-load-the-right-daemon.patch 2015-09-28 20:44:43.000000000 -0400 @@ -1,28 +1,25 @@ -From: David Steele <dste...@gmail.com> -Date: Mon, 14 Apr 2014 21:21:45 -0400 +From: David Steele <daves@jessie> +Date: Fri, 25 Sep 2015 22:41:02 -0400 Subject: Set Python path to load the right daemon -Some users are having a problem with the wrong daemon module -being loaded. Modify the Python path to ensure that -python-daemon is the one found. - -This uses Debian conventions, and is therefore Debian-only. +Some users were reporting problems with loading the wrong daemon module. +Modify the python path to ensure that python-daemon is the one found. --- cloudprint/cloudprint.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloudprint/cloudprint.py b/cloudprint/cloudprint.py -index f09a354..cdbf27f 100755 +index 2df3d58..f4a5d2d 100755 --- a/cloudprint/cloudprint.py +++ b/cloudprint/cloudprint.py -@@ -484,6 +484,10 @@ def main(): +@@ -483,6 +483,10 @@ def main(): - if daemon: + if args.daemon: try: + # workaround to avoid overloaded module + prepath = '/usr/lib/pymodules/python%s.%s' % sys.version_info[0:2] + sys.path.insert(0, prepath) + - from daemon import runner + import daemon + import daemon.pidfile except ImportError: - print 'daemon module required for -d' diff -Nru cloudprint-0.11/debian/patches/0003-Specify-dev-log-as-the-logging-target-for-daemon.patch cloudprint-0.13/debian/patches/0003-Specify-dev-log-as-the-logging-target-for-daemon.patch --- cloudprint-0.11/debian/patches/0003-Specify-dev-log-as-the-logging-target-for-daemon.patch 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/patches/0003-Specify-dev-log-as-the-logging-target-for-daemon.patch 2015-09-28 20:44:43.000000000 -0400 @@ -0,0 +1,22 @@ +From: David Steele <dste...@gmail.com> +Date: Thu, 9 Jul 2015 22:05:44 -0400 +Subject: Specify /dev/log as the logging target for daemon + +Debian disables the default UDP port, by default. +--- + cloudprint/cloudprint.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/cloudprint/cloudprint.py b/cloudprint/cloudprint.py +index f4a5d2d..295da5f 100755 +--- a/cloudprint/cloudprint.py ++++ b/cloudprint/cloudprint.py +@@ -432,7 +432,7 @@ def main(): + + # if daemon, log to syslog, otherwise log to stdout + if args.daemon: +- handler = logging.handlers.SysLogHandler() ++ handler = logging.handlers.SysLogHandler(address='/dev/log') + handler.setFormatter(logging.Formatter(fmt='cloudprint.py: %(message)s')) + else: + handler = logging.StreamHandler(sys.stdout) diff -Nru cloudprint-0.11/debian/patches/0004-Allow-older-python-requests.patch cloudprint-0.13/debian/patches/0004-Allow-older-python-requests.patch --- cloudprint-0.11/debian/patches/0004-Allow-older-python-requests.patch 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/patches/0004-Allow-older-python-requests.patch 2015-09-28 20:44:43.000000000 -0400 @@ -0,0 +1,25 @@ +From: David Steele <dste...@gmail.com> +Date: Fri, 25 Sep 2015 22:37:06 -0400 +Subject: Allow-older-python-requests + +Remove the version requirement on python-requests in setup.py. The tight +requirement caused deb-packaged versions to fail on older distributions. + +=================================================================== +--- + setup.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index 3403caf..5c754e4 100644 +--- a/setup.py ++++ b/setup.py +@@ -29,7 +29,7 @@ setup( + ], + }, + install_requires=[ +- 'requests >= 2.7.0', ++ 'requests', + 'pycups', + ], + extras_require={ diff -Nru cloudprint-0.11/debian/patches/0005-Revert-Update-for-new-python-deamon.patch cloudprint-0.13/debian/patches/0005-Revert-Update-for-new-python-deamon.patch --- cloudprint-0.11/debian/patches/0005-Revert-Update-for-new-python-deamon.patch 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/patches/0005-Revert-Update-for-new-python-deamon.patch 2015-09-28 20:44:43.000000000 -0400 @@ -0,0 +1,70 @@ +From: David Steele <daves@jessie> +Date: Fri, 25 Sep 2015 22:43:14 -0400 +Subject: Revert "Update for new python-deamon" + +Use the old python-daemon on Jessie. + +This reverts commit 88acead3d1daf1fd63ca32bb3eddf57ab392fea5. + +Conflicts: + cloudprint/cloudprint.py +--- + cloudprint/cloudprint.py | 32 ++++++++++++++++++++++++-------- + 1 file changed, 24 insertions(+), 8 deletions(-) + +diff --git a/cloudprint/cloudprint.py b/cloudprint/cloudprint.py +index 295da5f..7bb6969 100755 +--- a/cloudprint/cloudprint.py ++++ b/cloudprint/cloudprint.py +@@ -305,6 +305,21 @@ class PrinterProxy(object): + return self.cpp.delete_printer(self.id) + + ++class App(object): ++ def __init__(self, cups_connection=None, cpp=None, printers=None, pidfile_path=None): ++ self.cups_connection = cups_connection ++ self.cpp = cpp ++ self.printers = printers ++ self.pidfile_path = pidfile_path ++ self.stdin_path = '/dev/null' ++ self.stdout_path = '/dev/null' ++ self.stderr_path = '/dev/null' ++ self.pidfile_timeout = 5 ++ ++ def run(self): ++ process_jobs(self.cups_connection, self.cpp) ++ ++ + #True if printer name matches *any* of the regular expressions in regexps + def match_re(prn, regexps, empty=False): + if len(regexps): +@@ -487,20 +502,21 @@ def main(): + prepath = '/usr/lib/pymodules/python%s.%s' % sys.version_info[0:2] + sys.path.insert(0, prepath) + +- import daemon +- import daemon.pidfile ++ from daemon import runner + except ImportError: + print 'daemon module required for -d' + print '\tyum install python-daemon, or apt-get install python-daemon, or pip install cloudprint[daemon]' + sys.exit(1) + +- pidfile = daemon.pidfile.TimeoutPIDLockFile( +- path=os.path.abspath(args.pidfile), +- timeout=5, ++ # XXX printers is the google list ++ app = App( ++ cups_connection=cups_connection, ++ cpp=cpp, ++ pidfile_path=os.path.abspath(args.pidfile) + ) +- with daemon.DaemonContext(pidfile=pidfile): +- process_jobs(cups_connection, cpp) +- ++ sys.argv = [sys.argv[0], 'start'] ++ daemon_runner = runner.DaemonRunner(app) ++ daemon_runner.do_action() + else: + process_jobs(cups_connection, cpp) + diff -Nru cloudprint-0.11/debian/patches/0006-setup.py-specify-old-version-of-python-daemon.patch cloudprint-0.13/debian/patches/0006-setup.py-specify-old-version-of-python-daemon.patch --- cloudprint-0.11/debian/patches/0006-setup.py-specify-old-version-of-python-daemon.patch 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/patches/0006-setup.py-specify-old-version-of-python-daemon.patch 2015-09-28 20:44:43.000000000 -0400 @@ -0,0 +1,20 @@ +From: David Steele <dste...@gmail.com> +Date: Mon, 28 Sep 2015 19:00:55 -0400 +Subject: setup.py: specify old version of python-daemon + +--- + setup.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index 5c754e4..6a55d80 100644 +--- a/setup.py ++++ b/setup.py +@@ -33,6 +33,6 @@ setup( + 'pycups', + ], + extras_require={ +- 'daemon': ['python-daemon >= 2.0.0'], ++ 'daemon': ['python-daemon < 2.0.0'], + }, + ) diff -Nru cloudprint-0.11/debian/patches/series cloudprint-0.13/debian/patches/series --- cloudprint-0.11/debian/patches/series 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/patches/series 2015-09-28 20:44:43.000000000 -0400 @@ -1,18 +1,6 @@ 0001-Change-the-name-of-the-main-script-in-the-module.patch 0002-Set-Python-path-to-load-the-right-daemon.patch -0003-Disable-exception-processing-in-XMPP-close.patch -0004-First-functional-include-exclude.patch -0005-Code-cleanup-documentation.patch -0006-Documentation-and-minor-code-cleanup.patch -0007-Remove-debug-code.patch -0008-Use-x-from-exclude.patch -0009-Remove-whitespace-response-to-comment-from-armooo.patch -0010-Catch-exception-for-invalid-regular-expressions.patch -0011-cloudprint.py-Log-when-get_rest-changes-token.patch -0012-Remove-alternate-username-pw-auth-file-format.patch -0013-Add-u-option-to-store-username-password-with-sasl.patch -0014-Refresh-XMPP-authentication-in-process_jobs.patch -0015-Switch-from-getopt-to-argparse.patch -0016-Use-argparse-args.patch -0017-Fix-broken-reference-to-authfile.patch -0018-Add-copywrite-headers-to-source-files.patch +0003-Specify-dev-log-as-the-logging-target-for-daemon.patch +0004-Allow-older-python-requests.patch +0005-Revert-Update-for-new-python-deamon.patch +0006-setup.py-specify-old-version-of-python-daemon.patch diff -Nru cloudprint-0.11/debian/watch cloudprint-0.13/debian/watch --- cloudprint-0.11/debian/watch 2015-09-28 07:50:56.000000000 -0400 +++ cloudprint-0.13/debian/watch 2015-09-28 19:14:45.000000000 -0400 @@ -1,2 +1,2 @@ version=3 -https://pypi.python.org/packages/source/c/cloudprint/ cloudprint-(.+)\.tar\.gz +http://pypi.debian.net/cloudprint/ .+cloudprint-(.+)\.tar\.gz diff -Nru cloudprint-0.11/PKG-INFO cloudprint-0.13/PKG-INFO --- cloudprint-0.11/PKG-INFO 2014-01-05 19:53:29.000000000 -0500 +++ cloudprint-0.13/PKG-INFO 2015-07-08 23:17:23.000000000 -0400 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cloudprint -Version: 0.11 +Version: 0.13 Summary: Google cloud print proxy for linux/OSX Home-page: https://github.com/armooo/cloudprint Author: Jason Michalski @@ -9,10 +9,14 @@ Description: Share your CUPS printers with google's cloud print. Works with linux and OS X. + This software is a python implementation of a cloud print connector. Unlike + Google's linux connector, it does not require chrome to be installed on the server. + + Requires --------------------------------------------------- - python 2.6 or 2.7 - pycups (can be tricky on OS X) + - python 2.6 or 2.7 + - pycups (can be tricky on OS X) wich depends on libcups2-dev Usage --------------------------------------------------- @@ -24,10 +28,14 @@ -l : logout of the current google account -p pid_file : path to write the pid to (default cloudprint.pid) -a account_file : path to google account ident data (optional) - account_file format: <Google username> - <Google password> -c : establish and store login credentials, then exit -f : 'fast poll', if notifications aren't working + -u : store username/password in addition to login token + to avoid authentication expiration + -i regexp : include files matching regexp + -x regexp : exclude filees matching regexp + regexp: a Python regexp, which is matched against the + start of the printer name -h : display this help Google accounts with 2 step verification enabled need to use an @@ -43,12 +51,40 @@ Password: Added Printer Brother-HL-2170W + Examples - Include/Exclude + --------------------------------------------------- + + Include only the printers "`lp`" and "`2up`": + :: + + cloudprint -i lp -i 2up + + Exclude all printers whose names start with "`GCP-`": + :: + + cloudprint -x GCP- + + By default, all printers are included. For the include and exclude options, + the argument is a regular expression which is matched against the start of the + printer name. + + For example, to include all printers whose names begin "`lp`": + :: + + cloudprint -i lp # includes both lp and lp2up + + Install --------------------------------------------------- :: pip install cloudprint + or with optional daemon support + pip install cloudprint[daemon] + + After running cloudprint, verify that the connector successfully installed the cloud printer by visiting + http://www.google.com/cloudprint/manage.html. Platform: UNKNOWN Classifier: Development Status :: 4 - Beta diff -Nru cloudprint-0.11/README.rst cloudprint-0.13/README.rst --- cloudprint-0.11/README.rst 2014-01-05 19:29:25.000000000 -0500 +++ cloudprint-0.13/README.rst 2015-07-08 23:11:07.000000000 -0400 @@ -1,10 +1,14 @@ Share your CUPS printers with google's cloud print. Works with linux and OS X. +This software is a python implementation of a cloud print connector. Unlike +Google's linux connector, it does not require chrome to be installed on the server. + + Requires --------------------------------------------------- -python 2.6 or 2.7 -pycups (can be tricky on OS X) +- python 2.6 or 2.7 +- pycups (can be tricky on OS X) wich depends on libcups2-dev Usage --------------------------------------------------- @@ -16,10 +20,14 @@ -l : logout of the current google account -p pid_file : path to write the pid to (default cloudprint.pid) -a account_file : path to google account ident data (optional) - account_file format: <Google username> - <Google password> -c : establish and store login credentials, then exit -f : 'fast poll', if notifications aren't working + -u : store username/password in addition to login token + to avoid authentication expiration + -i regexp : include files matching regexp + -x regexp : exclude filees matching regexp + regexp: a Python regexp, which is matched against the + start of the printer name -h : display this help Google accounts with 2 step verification enabled need to use an @@ -35,9 +43,37 @@ Password: Added Printer Brother-HL-2170W +Examples - Include/Exclude +--------------------------------------------------- + +Include only the printers "`lp`" and "`2up`": +:: + + cloudprint -i lp -i 2up + +Exclude all printers whose names start with "`GCP-`": +:: + + cloudprint -x GCP- + +By default, all printers are included. For the include and exclude options, +the argument is a regular expression which is matched against the start of the +printer name. + +For example, to include all printers whose names begin "`lp`": +:: + + cloudprint -i lp # includes both lp and lp2up + + Install --------------------------------------------------- :: pip install cloudprint + or with optional daemon support + pip install cloudprint[daemon] + +After running cloudprint, verify that the connector successfully installed the cloud printer by visiting +http://www.google.com/cloudprint/manage.html. diff -Nru cloudprint-0.11/setup.py cloudprint-0.13/setup.py --- cloudprint-0.11/setup.py 2014-01-05 19:30:14.000000000 -0500 +++ cloudprint-0.13/setup.py 2015-07-08 23:16:45.000000000 -0400 @@ -5,17 +5,15 @@ use_setuptools() from setuptools import setup, find_packages -import os.path - setup( name='cloudprint', - version='0.11', + version='0.13', description='Google cloud print proxy for linux/OSX', long_description=open('README.rst').read(), author='Jason Michalski', author_email='arm...@armooo.net', url='https://github.com/armooo/cloudprint', - classifiers = [ + classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: End Users/Desktop', @@ -25,9 +23,16 @@ 'License :: OSI Approved :: GNU General Public License (GPL)', ], packages=find_packages(exclude=['ez_setup']), - entry_points = { + entry_points={ 'console_scripts': [ 'cloudprint = cloudprint.cloudprint:main', ], }, + install_requires=[ + 'requests >= 2.7.0', + 'pycups', + ], + extras_require={ + 'daemon': ['python-daemon >= 2.0.0'], + }, )
signature.asc
Description: OpenPGP digital signature