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