Package: release.debian.org Severity: normal Tags: jessie User: release.debian....@packages.debian.org Usertags: pu
Google has removed support for SASL authentication for Cloud Print services, and is now requiring OAuth2 authentication. This breaks the version of cloudprint which is in Jessie, making the package totally non-functional. I attempted a targeted patch of upstream changes to pull in only OAuth2-relevant content. It was neither clean, concise, nor stable. Instead, I am proposing to modify the 3-month-old upstream 0.13 release for Jessie. Changes required: - revert to support for the older python-daemon module - remove a setup.py version restriction on 'requests' Debdiff attached, with patch detail on deleted files removed. The change relative to 0.13 is here: https://github.com/davesteele/cloudprint-service/compare/debian/0.13-1...updates-jessie -- System Information: Debian Release: stretch/sid APT prefers unstable APT policy: (500, 'unstable') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 4.0.0-2-amd64 (SMP w/6 CPU cores) Locale: LANG=en_US.utf8, LC_CTYPE=en_US.utf8 (charmap=UTF-8)
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-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/changelog 2015-09-27 10:39:41.000000000 -0400 @@ -1,3 +1,71 @@ +cloudprint (0.13-1+deb8u3) UNRELEASED; urgency=medium + + * Modify for Jessie + - Brings OAuth support to Jessie. + (Closes: 799227, 800082) + - Remove version requirement on python-requests. + - Revert back to support for older python-daemon module. + + -- 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-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint.man.in 2015-09-27 10:39:41.000000000 -0400 @@ -1,15 +1,15 @@ -.\" Copyright 2013-2014 David Steele <dste...@gmail.com> +.\" Copyright 2013-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 PROG 1 2014-04-16 Linux "User Commands" +.TH cloudprint 1 2015-05-31 Linux "User Commands" .SH NAME -PROG \- share CUPS printers with Google Cloud Print +cloudprint \- share CUPS printers with Google Cloud Print .SH SYNOPSIS -\fBPROG\fP [\fIOPTION\fP]... +\fBcloudprint\fP [\fIOPTION\fP]... .SH DESCRIPTION -The \fBPROG\fP script makes locally defined CUPS printers available to +The \fBcloudprint\fP script makes locally defined CUPS printers available to a Google account, enabling local or remote printing from such services as the Chrome browser, Chrome OS devices, or Android devices. @@ -38,13 +38,13 @@ .TP \fB\-l\fR log out of the Google account. This deletes the account identification file, requiring reauthentication the next time -PROG is run. +cloudprint is run. .TP \fB\-p\fR \fI<pid_file>\fR path to PID file in daemon mode (default \fIcloudprint.pid\fP). .TP -\fB\-u\fR -store username/password in addition to login token to avoid authentication expiration. +\fB\-u\fR \fI<user name>\fR +the Google service user name to use if authentication is needed. .TP \fB\-v\fR verbose logging. @@ -55,9 +55,8 @@ \fB\-h\fR display help .PP -\fBPROG\fR will prompt for a username and password if the information has not been provided. +\fBcloudprint\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 @@ -68,4 +67,4 @@ Default location for storing internal authentication tokens .SH SEE ALSO -.BR cloudprint-service(7), cloudprintd(8), cloudprint(1) +.BR cloudprint-service(7), cloudprintd(8), cloudprint(1), cps-auth(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-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint-service.7 2015-09-27 10:39:41.000000000 -0400 @@ -1,38 +1,25 @@ -.\" (C) Copyright 2013-2014 David Steele <dste...@gmail.com>, +.\" (C) Copyright 2013-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 CLOUDPRINT-SERVICE 7 2014-04-16 Linux "System Commands" +.TH CLOUDPRINT-SERVICE 7 2015-05-31 Linux "System Commands" .SH NAME cloudprint-service \- manage the Google Cloud Print proxy .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. -.TP -service \fBcloudprintd\fR \fIstart\fR +systemctl \fIstart\fR \fBcloudprintd.service\fR Start the \fBcloudprintd\fR service. This will fail if the credentials have not been established. The service will normally start automatically when the computer is running. .TP -service \fBcloudprintd\fR \fIstop\fR +systemctl \fIstop\fR \fBcloudprintd.service\fR Stop the \fBcloudprintd\fR service. Login credentials are not lost. Normally, the printer will continue to be shown as a valid destination. Any jobs spooled during that time will print when the service is restarted. .TP -service \fBcloudprintd\fR \fIstatus\fR +systemctl \fIstatus\fR \fBcloudprintd.service\fR Determine whether the service is running or not. -.TP -service \fBcloudprintd\fR \fIlogout\fR -Delete the login credentials such that the service cannot be restarted without -another 'login'. - -.P -(...) .SH DESCRIPTION The \fBcloudprintd\fR services manages the operation of a Google Cloud Print @@ -43,6 +30,8 @@ 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 +39,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.service cloudprint-0.13/debian/cloudprint-service.cloudprintd.service --- cloudprint-0.11/debian/cloudprint-service.cloudprintd.service 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/cloudprint-service.cloudprintd.service 2015-09-27 10:39:41.000000000 -0400 @@ -0,0 +1,12 @@ +[Unit] +Description=Google Cloud Print proxy service +After=cups.service + +[Service] +ExecStart=/usr/sbin/cloudprintd -a /var/lib/cloudprintd/authfile.json +Type=simple +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target 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-27 10:39:41.000000000 -0400 @@ -0,0 +1 @@ +debian/cps-auth usr/bin diff -Nru cloudprint-0.11/debian/cloudprint-service.maintscript cloudprint-0.13/debian/cloudprint-service.maintscript --- cloudprint-0.11/debian/cloudprint-service.maintscript 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/cloudprint-service.maintscript 2015-09-27 10:39:41.000000000 -0400 @@ -0,0 +1,2 @@ +rm_conffile /etc/init.d/cloudprintd 0.12-3~ + 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-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint-service.manpages 2015-09-27 10:39:41.000000000 -0400 @@ -1,2 +1,3 @@ debian/cloudprint-service.7 debian/cloudprintd.8 +debian/cps-auth.1 diff -Nru cloudprint-0.11/debian/cloudprint-service.postinst cloudprint-0.13/debian/cloudprint-service.postinst --- cloudprint-0.11/debian/cloudprint-service.postinst 2015-09-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/cloudprint-service.postinst 2015-09-27 10:39:41.000000000 -0400 @@ -33,6 +33,11 @@ rmdir /var/lib/cloudprint ; \ fi + if [ -e /etc/init.d/cloudprintd/authfile.json \ + -a -e /etc/init.d/cloudprintd/authfile ] ; then \ + rm -f /etc/init.d/cloudprintd/authfile; + fi + ;; abort-upgrade|abort-remove|abort-deconfigure) diff -Nru cloudprint-0.11/debian/control cloudprint-0.13/debian/control --- cloudprint-0.11/debian/control 2015-09-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/control 2015-09-27 10:39:41.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-systemd (>= 1.5), 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, ${misc:Depends}, + python (>= 2.7), 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 @@ -21,8 +23,7 @@ Package: cloudprint-service Architecture: all Provides: cloudprintd -Depends: cloudprint, ${misc:Depends}, - initscripts (>= 2.88dsf-13.3) +Depends: cloudprint, ${misc:Depends}, systemd 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-27 10:39:41.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-27 10:39:41.000000000 -0400 @@ -0,0 +1,27 @@ +.\" 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 [\fI<user name>\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), +cps-auth(1) + diff -Nru cloudprint-0.11/debian/gbp.conf cloudprint-0.13/debian/gbp.conf --- cloudprint-0.11/debian/gbp.conf 2015-09-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/gbp.conf 2015-09-27 10:39:41.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/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-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/patches/0001-Change-the-name-of-the-main-script-in-the-module.patch 2015-09-27 10:39:41.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-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/patches/0002-Set-Python-path-to-load-the-right-daemon.patch 2015-09-27 10:39:41.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-27 10:39:41.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-27 10:39:41.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-27 10:39:41.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 Jessit. + +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/series cloudprint-0.13/debian/patches/series --- cloudprint-0.11/debian/patches/series 2015-09-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/patches/series 2015-09-27 10:39:41.000000000 -0400 @@ -1,18 +1,5 @@ 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 diff -Nru cloudprint-0.11/debian/rules cloudprint-0.13/debian/rules --- cloudprint-0.11/debian/rules 2015-09-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/rules 2015-09-27 10:39:41.000000000 -0400 @@ -4,7 +4,7 @@ # Tue, 23 Apr 2013 19:42:12 -0400 %: - dh $@ --with python2 + dh $@ --with python2,systemd override_dh_auto_install: python setup.py install --root=debian/cloudprint --install-layout=deb --install-lib=/usr/share/cloudprint --install-scripts=/usr/share/cloudprint @@ -15,7 +15,7 @@ /bin/sed 's/cloudprintd 1/cloudprintd 8/' debian/cloudprintd.8 >debian/cloudprintd.8.tmp /bin/mv debian/cloudprintd.8.tmp debian/cloudprintd.8 +override_dh_systemd_enable: + dh_systemd_enable --name=cloudprintd -override_dh_installinit: - dh_installinit --name=cloudprintd diff -Nru cloudprint-0.11/debian/TODO cloudprint-0.13/debian/TODO --- cloudprint-0.11/debian/TODO 1969-12-31 19:00:00.000000000 -0500 +++ cloudprint-0.13/debian/TODO 2015-09-27 10:39:41.000000000 -0400 @@ -0,0 +1,4 @@ + +- restore INCLUDE/EXCLUDE with a systemd wrapper script +- replace GNOME requirement with X, by using webbrowser and xwininfo +- merge with armoo OAUth2 diff -Nru cloudprint-0.11/debian/watch cloudprint-0.13/debian/watch --- cloudprint-0.11/debian/watch 2015-09-27 10:54:15.000000000 -0400 +++ cloudprint-0.13/debian/watch 2015-09-27 10:39:41.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'], + }, )