[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#800163: jessie-pu: package cloudprint/0.11-5



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 <armooo@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 <armooo@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 <armooo@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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 <dsteele@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='armooo@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'],
+    },
 )

Attachment: signature.asc
Description: OpenPGP digital signature


Reply to: