diff --git a/services/update.py b/services/update.py
index 9e33151f3d..6aa17935ee 100644
--- a/services/update.py
+++ b/services/update.py
@@ -305,6 +305,212 @@ class Update(object):
('Content-Length', str(length))]
+# TODO: Delete this (and settings toggle below) after new Update class testing.
+class OldUpdate(object):
+
+ def __init__(self, data):
+ self.conn, self.cursor = None, None
+ self.data = data.copy()
+ self.data['row'] = {}
+ self.flags = {'use_version': False, 'multiple_status': False}
+ self.is_beta_version = False
+ self.version_int = 0
+
+ def is_valid(self):
+ # If you accessing this from unit tests, then before calling
+ # is valid, you can assign your own cursor.
+ if not self.cursor:
+ self.conn = mypool.connect()
+ self.cursor = self.conn.cursor()
+
+ data = self.data
+
+ for field in ['reqVersion', 'id', 'version', 'appID', 'appVersion']:
+ if field not in data:
+ return False
+
+ data['app_id'] = APP_GUIDS.get(data['appID'])
+ if not data['app_id']:
+ return False
+
+ sql = """SELECT id, status, addontype_id, guid FROM addons
+ WHERE guid = %(guid)s AND inactive = 0 LIMIT 1;"""
+ self.cursor.execute(sql, {'guid': self.data['id']})
+ result = self.cursor.fetchone()
+ if result is None:
+ return False
+
+ data['id'], data['addon_status'], data['type'], data['guid'] = result
+ data['version_int'] = version_int(data['appVersion'])
+
+ if 'appOS' in data:
+ for k, v in PLATFORMS.items():
+ if k in data['appOS']:
+ data['appOS'] = v
+ break
+ else:
+ data['appOS'] = None
+
+ self.is_beta_version = base.VERSION_BETA.search(data.get('version',
+ ''))
+ return True
+
+ def get_beta(self):
+ data = self.data
+ data['status'] = base.STATUS_PUBLIC
+
+ if data['addon_status'] == base.STATUS_PUBLIC:
+ # Beta channel looks at the addon name to see if it's beta.
+ if self.is_beta_version:
+ # For beta look at the status of the existing files.
+ sql = """
+ SELECT versions.id, status
+ FROM files INNER JOIN versions
+ ON files.version_id = versions.id
+ WHERE versions.addon_id = %(id)s
+ AND versions.version = %(version)s LIMIT 1;"""
+ self.cursor.execute(sql, data)
+ result = self.cursor.fetchone()
+ # Only change the status if there are files.
+ if result is not None:
+ status = result[1]
+ # If it's in Beta or Public, then we should be looking
+ # for similar. If not, find something public.
+ if status in (base.STATUS_BETA, base.STATUS_PUBLIC):
+ data['status'] = status
+ else:
+ data.update(STATUSES_PUBLIC)
+ self.flags['multiple_status'] = True
+
+ elif data['addon_status'] in (base.STATUS_LITE,
+ base.STATUS_LITE_AND_NOMINATED):
+ data['status'] = base.STATUS_LITE
+ else:
+ # Otherwise then we'll keep the update within the current version.
+ data['status'] = base.STATUS_NULL
+ self.flags['use_version'] = True
+
+ def get_update(self):
+ self.get_beta()
+ data = self.data
+
+ sql = """
+ SELECT
+ addons.guid as guid, addons.addontype_id as type,
+ addons.inactive as disabled_by_user,
+ applications.guid as appguid, appmin.version as min,
+ appmax.version as max, files.id as file_id,
+ files.status as file_status, files.hash,
+ files.filename, versions.id as version_id,
+ files.datestatuschanged as datestatuschanged,
+ versions.releasenotes, versions.version as version,
+ addons.premium_type
+ FROM versions
+ INNER JOIN addons
+ ON addons.id = versions.addon_id AND addons.id = %(id)s
+ INNER JOIN applications_versions
+ ON applications_versions.version_id = versions.id
+ INNER JOIN applications
+ ON applications_versions.application_id = applications.id
+ AND applications.id = %(app_id)s
+ INNER JOIN appversions appmin
+ ON appmin.id = applications_versions.min
+ INNER JOIN appversions appmax
+ ON appmax.id = applications_versions.max
+ INNER JOIN files
+ ON files.version_id = versions.id AND (files.platform_id = 1
+ """
+ if data.get('appOS'):
+ sql += ' OR files.platform_id = %(appOS)s'
+
+ if self.flags['use_version']:
+ sql += (') WHERE files.status > %(status)s AND '
+ 'versions.version = %(version)s ')
+ else:
+ if self.flags['multiple_status']:
+ # Note that getting this properly escaped is a pain.
+ # Suggestions for improvement welcome.
+ sql += (') WHERE files.status in (%(STATUS_PUBLIC)s,'
+ '%(STATUS_LITE)s,%(STATUS_LITE_AND_NOMINATED)s)')
+ else:
+ sql += ') WHERE files.status = %(status)s '
+
+ sql += """
+ AND (appmin.version_int <= %(version_int)s
+ AND appmax.version_int >= %(version_int)s)
+ ORDER BY versions.id DESC LIMIT 1;
+ """
+
+ self.cursor.execute(sql, data)
+ result = self.cursor.fetchone()
+ if result:
+ row = dict(zip([
+ 'guid', 'type', 'disabled_by_user', 'appguid', 'min', 'max',
+ 'file_id', 'file_status', 'hash', 'filename', 'version_id',
+ 'datestatuschanged', 'releasenotes', 'version',
+ 'premium_type'],
+ list(result)))
+ row['type'] = base.ADDON_SLUGS_UPDATE[row['type']]
+ if row['premium_type'] == base.ADDON_PREMIUM:
+ qs = urlencode(dict((k, data.get(k, ''))
+ for k in base.WATERMARK_KEYS))
+ row['url'] = (u'%s/downloads/watermarked/%s?%s' %
+ (settings.SITE_URL, row['file_id'], qs))
+ else:
+ row['url'] = get_mirror(self.data['addon_status'],
+ self.data['id'], row)
+ data['row'] = row
+ return True
+
+ return False
+
+ def get_bad_rdf(self):
+ return bad_rdf
+
+ def get_rdf(self):
+ if self.is_valid():
+ if self.get_update():
+ rdf = self.get_good_rdf()
+ else:
+ rdf = self.get_no_updates_rdf()
+ else:
+ rdf = self.get_bad_rdf()
+ self.cursor.close()
+ if self.conn:
+ self.conn.close()
+ return rdf
+
+ def get_no_updates_rdf(self):
+ name = base.ADDON_SLUGS_UPDATE[self.data['type']]
+ return no_updates_rdf % ({'guid': self.data['guid'], 'type': name})
+
+ def get_good_rdf(self):
+ data = self.data['row']
+ data['if_hash'] = ''
+ if data['hash']:
+ data['if_hash'] = ('%s' %
+ data['hash'])
+
+ data['if_update'] = ''
+ if data['releasenotes']:
+ data['if_update'] = ('%s%s%s/%%APP_LOCALE%%/'
+ '' %
+ (settings.SITE_URL, '/versions/updateInfo/',
+ data['version_id']))
+
+ return good_rdf % data
+
+ def format_date(self, secs):
+ return '%s GMT' % formatdate(time() + secs)[:25]
+
+ def get_headers(self, length):
+ return [('Content-Type', 'text/xml'),
+ ('Cache-Control', 'public, max-age=3600'),
+ ('Last-Modified', self.format_date(0)),
+ ('Expires', self.format_date(3600)),
+ ('Content-Length', str(length))]
+
+
def mail_exception(data):
if settings.EMAIL_BACKEND != 'django.core.mail.backends.smtp.EmailBackend':
return
@@ -335,7 +541,10 @@ def application(environ, start_response):
data = dict(parse_qsl(environ['QUERY_STRING']))
compat_mode = data.pop('compatMode', 'strict')
try:
- update = Update(data, compat_mode)
+ if settings.DEFAULT_TO_COMPATIBLE:
+ update = Update(data, compat_mode)
+ else:
+ update = OldUpdate(data)
output = update.get_rdf()
start_response(status, update.get_headers(len(output)))
except:
diff --git a/settings.py b/settings.py
index e7e89c57f9..6841a291a0 100644
--- a/settings.py
+++ b/settings.py
@@ -1318,3 +1318,7 @@ NO_ADDONS_MODULES = (
# Cron jobs that aren't critical will check this flag and not run if this
# is True.
IGNORE_NON_CRITICAL_CRONS = False
+
+# To enable new default to compatible checks in services/update.py set to True.
+# Set to False in case of emergency to switch back to old code.
+DEFAULT_TO_COMPATIBLE = True