_get_version()   C
last analyzed

Complexity

Conditions 7

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
c 0
b 0
f 0
dl 0
loc 31
rs 5.5
1
# coding: utf-8
2
3
"""
4
This software is licensed under the Apache 2 license, quoted below.
5
6
Copyright 2014 Crystalnix Limited
7
8
Licensed under the Apache License, Version 2.0 (the "License"); you may not
9
use this file except in compliance with the License. You may obtain a copy of
10
the License at
11
12
    http://www.apache.org/licenses/LICENSE-2.0
13
14
Unless required by applicable law or agreed to in writing, software
15
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17
License for the specific language governing permissions and limitations under
18
the License.
19
"""
20
21
from builtins import filter
22
from functools import partial, reduce
23
from uuid import UUID
24
25
from django.utils.timezone import now
26
from django.db.models import Q
27
28
from lxml import etree
29
from cacheops import cached_as
30
31
from omaha import tasks
32
from omaha.models import Version
33
from omaha.parser import parse_request
34
from omaha import parser
35
from omaha.statistics import is_user_active
36
from omaha.core import (Response, App, Updatecheck_negative, Manifest, Updatecheck_positive,
37
                  Packages, Package, Actions, Action, Event, Data)
38
39
40
__all__ = ['build_response']
41
42
43
def on_event(event_list, event):
44
    event_list.append(Event())
45
    return event_list
46
47
48
def on_data(data_list, data, version):
49
    name = data.get('name')
50
    if name == 'untrusted':
51
        _data = Data('untrusted')
52
    elif name == 'install':
53
        index = data.get('index')
54
        data_obj_list = filter(lambda d: d.index == index, version.app.data_set.all())
55
        try:
56
            _data = Data('install', index=index, text=next(data_obj_list).value)
57
        except StopIteration:
58
            _data = Data('install', index=index, status='error-nodata')
59
60
    data_list.append(_data)
61
    return data_list
62
63
64
def on_action(action_list, action):
65
    action = Action(
66
        event=action.get_event_display(),
67
        **action.get_attributes()
68
    )
69
    action_list.append(action)
70
    return action_list
71
72
73
def is_new_user(version):
74
    if version == '':
75
        return True
76
    return False
77
78
79
@cached_as(Version, timeout=60)
80
def _get_version(partialupdate, app_id, platform, channel, version, date=None):
81
    date = date or now()
82
83
    qs = Version.objects.select_related('app')
84
    qs = qs.filter_by_enabled(app=app_id,
85
                              platform__name=platform,
86
                              channel__name=channel)
87
    qs = qs.filter(version__gt=version) if version else qs
88
    qs = qs.prefetch_related("actions", "partialupdate")
89
90
    if partialupdate:
91
        try:
92
            qs = qs.filter(partialupdate__is_enabled=True,
93
                           partialupdate__start_date__lte=date,
94
                           partialupdate__end_date__gte=date)
95
            critical_version = qs.filter(is_critical=True).order_by('version').cache().first()
96
            new_version = qs.cache().latest('version')
97
        except Version.DoesNotExist:
98
            return None
99
    else:
100
        qs = qs.filter(Q(partialupdate__isnull=True)
101
                       | Q(partialupdate__is_enabled=False))
102
        try:
103
            critical_version = qs.filter(is_critical=True).order_by('version').cache().first()
104
            new_version = qs.cache().latest('version')
105
        except:
106
            raise Version.DoesNotExist
107
    if not is_new_user(version) and critical_version:
108
        return critical_version
109
    return new_version
110
111
112
def get_version(app_id, platform, channel, version, userid, date=None):
113
    try:
114
        new_version = _get_version(True, app_id, platform, channel, version, date=date)
115
116
        if not new_version:
117
            raise Version.DoesNotExist
118
119
        if new_version.partialupdate.exclude_new_users and is_new_user(version):
120
            raise Version.DoesNotExist
121
122
        if not is_user_active(new_version.partialupdate.active_users, userid):
123
            raise Version.DoesNotExist
124
125
        userid_int = UUID(userid).int
126
        percent = new_version.partialupdate.percent
127
        if not (userid_int % int(100 / percent)) == 0:
128
            raise Version.DoesNotExist
129
    except Version.DoesNotExist:
130
        new_version = _get_version(False, app_id, platform, channel, version, date=date)
131
132
    return new_version
133
134
135
def on_app(apps_list, app, os, userid):
136
    app_id = app.get('appid')
137
    version = app.get('version')
138
    platform = os.get('platform')
139
    channel = parser.get_channel(app)
140
    ping = bool(app.findall('ping'))
141
    events = reduce(on_event, app.findall('event'), [])
142
    build_app = partial(App, app_id, status='ok', ping=ping, events=events)
143
    updatecheck = app.findall('updatecheck')
144
145
    try:
146
        version = get_version(app_id, platform, channel, version, userid)
147
    except Version.DoesNotExist:
148
        apps_list.append(
149
            build_app(updatecheck=Updatecheck_negative() if updatecheck else None))
150
        return apps_list
151
152
    data_list = reduce(partial(on_data, version=version), app.findall('data'), [])
153
    build_app = partial(build_app, data_list=data_list)
154
155
    if updatecheck:
156
        actions = reduce(on_action, version.actions.all(), [])
157
        updatecheck = Updatecheck_positive(
158
            urls=[version.file_url],
159
            manifest=Manifest(
160
                version=str(version.version),
161
                packages=Packages([Package(
162
                    name=version.file_package_name,
163
                    required='true',
164
                    size=str(version.file_size),
165
                    hash=version.file_hash,
166
                )]),
167
                actions=Actions(actions) if actions else None,
168
            )
169
        )
170
        apps_list.append(build_app(updatecheck=updatecheck))
171
    else:
172
        apps_list.append(build_app())
173
174
    return apps_list
175
176
177
def build_response(request, pretty_print=True, ip=None):
178
    obj = parse_request(request)
179
    tasks.collect_statistics.apply_async(args=(request, ip), queue='transient')
180
    userid = obj.get('userid')
181
    apps = obj.findall('app')
182
    apps_list = reduce(partial(on_app, os=obj.os, userid=userid), apps, [])
183
    response = Response(apps_list, date=now())
184
    return etree.tostring(response, pretty_print=pretty_print,
185
                          xml_declaration=True, encoding='UTF-8')
186