Package mobi :: Package mtld :: Package da :: Package device :: Module client_props
[hide private]
[frames] | no frames]

Source Code for Module mobi.mtld.da.device.client_props

  1  #!/usr/bin/env python 
  2   
  3  """ 
  4  @copyright: 
  5   Copyright (c) 2014 by mTLD Top Level Domain Limited. All rights reserved.\n 
  6   Portions copyright (c) 2008 by Argo Interactive Limited.\n 
  7   Portions copyright (c) 2008 by Nokia Inc.\n 
  8   Portions copyright (c) 2008 by Telecom Italia Mobile S.p.A.\n 
  9   Portions copyright (c) 2008 by Volantis Systems Limited.\n 
 10   Portions copyright (c) 2002-2008 by Andreas Staeding.\n 
 11   Portions copyright (c) 2008 by Zandan.\n 
 12  @author: dotMobi 
 13  """ 
 14   
 15  from mobi.mtld.da.device.client_props_rule_set import ClientPropsRuleSet 
 16  from mobi.mtld.da.device.post_walk_rules import PostWalkRules 
 17  from mobi.mtld.da.property import Property 
 18  from mobi.mtld.da.exception.client_properties_exception import ClientPropertiesException 
 19   
20 -class ClientProps(PostWalkRules):
21 22 # Constants to make keys more readable 23 KEY_CP_RULES = 'cpr' 24 KEY_USER_AGENT = 'ua' 25
26 - def __init__(self, tree):
27 super(ClientProps, self).__init__(tree, self.KEY_CP_RULES)
28
29 - def put_properties(self, client_side_properties):
30 """ 31 Put Client Side properties into the property set. 32 33 The first character of the property name is the type of the value. 34 35 @param client_side_properties: Cookie content with the following format: 36 bjs.webGl:1|sdeviceAspectRatio:16/10|iusableDisplayHeight:1050 37 """ 38 39 # "merge with detected properties" and "get" the props from the client side 40 # cookie 41 client_side_properties = self.parse_client_side_properties( 42 client_side_properties) 43 44 # use the merged properties to look up additional rules 45 46 # STEP 1: try and find the rules to run on the UA 47 rule_groups = self.branch[self.KEY_RULE_GROUPS] 48 rules_to_run = self.__rules_to_run(rule_groups) 49 50 # STEP 2: do second tree walk if necessary and replace/create any new 51 # values based on the rules 52 if rules_to_run is None: 53 return 54 55 user_agent = rules_to_run.user_agent 56 57 if user_agent is not None: 58 59 # use the UA for a second tree walk - note the last param is 60 # false as we know the UA won't have any dynamic properties 61 # also sought = nil 62 self.tree_provider.put_tree_walk_properties(user_agent) 63 64 # merge origProperties in to get any parent properties such as the dynamic 65 # properties 2nd tree walk > first tree walk 66 67 # the client properties still take priority so merge them in again 68 # client props > tree walks 69 for name, value in client_side_properties.items(): 70 self.tree_provider.properties[name[1:]] = Property(value, name[0]) 71 72 # overlay the new properties 73 rule_set = rules_to_run.rule_set 74 for prop_id_val_id in rule_set: 75 name = self.tree_provider.property_name_by_id( 76 prop_id_val_id[self.KEY_PROPERTY_MATCHER]) 77 value = self.tree_provider.property_value_by_id( 78 prop_id_val_id[self.KEY_PROPERTY_VALUE]) 79 self.tree_provider.properties[name[1:]] = Property(value, name[0])
80 81
82 - def parse_client_side_properties(self, client_side_properties):
83 """ 84 Parse the client side properties and if typed values is set convert the 85 values to the appropriate type. 86 87 The first character of the property name is the type of the value. 88 89 @param client_side_properties: Cookie content with the form: 90 bjs.webGl:1|sdeviceAspectRatio:16/10|iusableDisplayHeight:1050 91 """ 92 93 # Table to replace HTML escape characters from property values in the cookie 94 # content. 95 html_escape_characters = {'&': '&amp;', '"': '&quot;', '<': '&lt;', 96 '>': '&gt;'} 97 98 props = {} 99 client_side_properties = client_side_properties.strip() 100 101 try: 102 103 name = '' 104 value = '' 105 is_key = True 106 is_type = True 107 key_ok = True 108 type = 0 109 110 num_chars = len(client_side_properties) - 1 111 112 # iterate over the property string looking for properties and values 113 max_i = num_chars + 1 114 for i in range(0,max_i): # Loop from 0 to num_chars-1 115 116 c = client_side_properties[i] 117 118 if c == '|': 119 120 # if key is valid add property 121 if key_ok: 122 type = name[0] 123 props[name] = value # No need of typifying it 124 self.tree_provider.properties[name[1:]] = Property(value, type) 125 126 # reset for next key/value 127 name = '' 128 value = '' 129 is_key = True 130 key_ok = True 131 is_type = True 132 133 continue # skip to the next character 134 135 elif c == ':': 136 137 is_key = False 138 continue # skip to the next character 139 140 elif c == '"': 141 142 if i < num_chars: 143 next_c = client_side_properties[i+1] 144 else: 145 next_c = 0 146 147 if (i == 0 or i == num_chars or is_key or len(value) == 0 or 148 next_c == '|' or next_c == '"'): 149 continue # skip any wrapping quotes 150 151 # end if c == '|' 152 153 if is_key: 154 # check if property type and name are correct 155 if is_type: 156 if c != 'b' and c != 'i' and c != 's' and c != 'd': 157 key_ok = False 158 is_type = False 159 else: 160 c_ord = ord(c) 161 if ((c_ord < 48 and c_ord != 46) or c_ord > 122 or 162 (c_ord > 57 and c_ord < 65) or (c_ord > 90 and c_ord < 97)): 163 key_ok = False 164 name += c 165 else: 166 if c in html_escape_characters: 167 value += html_escape_characters[c] 168 else: 169 value += c 170 # end of for 171 172 # add the last prop value 173 if key_ok: 174 type = name[0] 175 props[name] = value # No need of typifying it 176 self.tree_provider.properties[name[1:]] = Property(value, type) 177 178 except Exception as e: 179 raise ClientPropertiesException( 180 "Could not decode client properties: %s" % e.message) 181 182 return props
183 184 # Protected 185
186 - def _init_get_matcher_propery_ids(self, group, prop_ids):
187 """ 188 Find all the properties that are used for matching. 189 190 @param group: The rule group that can contain a property matcher. 191 @param prop_ids: The set of found property IDs. 192 It returns an updated set of property IDs. 193 """ 194 if group[self.KEY_PROPERTY_MATCHER]: 195 property_matchers = group[self.KEY_PROPERTY_MATCHER] 196 for property_matcher in property_matchers: 197 prop_id = property_matcher[self.KEY_PROPERTY_MATCHER] 198 if prop_id not in prop_ids: 199 prop_ids.append(prop_id) 200 return prop_ids
201
202 - def _init_rule_sets(self, group):
203 """ 204 Prepare the rule set by extracting it from the current group and wrapping 205 it in a list. 206 207 @param group: The current parent group. 208 It returns a list of all rule sets. 209 """ 210 # wrap the single rule set in an array list. 211 return [{ self.KEY_RULE_ARR: group[self.KEY_RULE_ARR] }]
212 213 # Private 214
215 - def __rules_to_run(self, groups):
216 217 for group in groups: 218 219 property_matchers = group[self.KEY_PROPERTY_MATCHER] 220 221 # try matching defined properties so we know what rules to run. If there 222 # is a match then we can return the rules to run. 223 prop_match = self.__check_properties_match(property_matchers) 224 225 if prop_match: 226 if self.KEY_USER_AGENT in group: 227 user_agent = group[self.KEY_USER_AGENT] 228 else: 229 user_agent = '' 230 rule_set = group[self.KEY_RULE_ARR] 231 return ClientPropsRuleSet(user_agent, rule_set) 232 233 return None
234
235 - def __check_properties_match(self, property_matchers):
236 237 # loop over property_matchers and try and match ALL properties 238 for matcher_details in property_matchers: 239 240 prop_id = matcher_details[self.KEY_PROPERTY_MATCHER] 241 prop_name_type = self.tree_provider.property_name_by_id(prop_id) 242 prop_name = prop_name_type[1:] 243 244 # compare the detected value to the expected value 245 if prop_name in self.tree_provider.properties: 246 247 detected_property = self.tree_provider.properties.get(prop_name) 248 249 # get the expected value 250 prop_val_id = matcher_details[self.KEY_PROPERTY_VALUE] 251 expected_value = self.tree_provider.property_value_by_id(prop_val_id) 252 operator = matcher_details[self.KEY_OPERATOR] 253 254 # It is important to compare the property values with the same type 255 if not self.__compare_values( 256 str(detected_property.value), 257 str(expected_value), 258 operator, 259 prop_name_type[0]): 260 return False 261 262 else: 263 return False 264 265 return True
266
267 - def __compare_values(self, detected_value, expected_value, operator, 268 type_prop_name):
269 result = False 270 operator_equals = '=' 271 operator_not_equals = '!=' 272 operator_less_than = '<' 273 operator_less_than_equals = '<=' 274 operator_greater_than = '>' 275 operator_greater_than_equals = '>=' 276 277 t = type_prop_name[0] 278 279 if t in ('s', 'b'): 280 if operator == operator_equals: 281 result = (detected_value == expected_value) 282 elif operator == operator_not_equals: 283 result = (not detected_value == expected_value) 284 elif t == 'i': 285 d_val = int(detectedValue) 286 e_val = int(expectedValue) 287 288 if d_val == e_val and (operator in(operator_equals, 289 operator_less_than_equals, operator_greater_than_equals)): 290 result = True 291 elif d_val > e_val and (operator in(operator_greater_than, 292 operator_greater_than_equals)): 293 result = True 294 elif d_val < e_val and (operator in(operator_less_than, 295 operator_less_than_equals)): 296 result = True 297 elif d_val != e_val and operator == operator_not_equals: 298 result = True 299 300 return result
301