OLD | NEW |
---|---|
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """An implementation of the server side of the Chromium sync protocol. | 5 """An implementation of the server side of the Chromium sync protocol. |
6 | 6 |
7 The details of the protocol are described mostly by comments in the protocol | 7 The details of the protocol are described mostly by comments in the protocol |
8 buffer definition at chrome/browser/sync/protocol/sync.proto. | 8 buffer definition at chrome/browser/sync/protocol/sync.proto. |
9 """ | 9 """ |
ncarter (slow)
2012/02/28 18:45:09
Did you run chromiumsync_test.py? It has pretty go
tim (not reviewing)
2012/03/01 22:07:02
Done.
| |
10 | 10 |
11 import cgi | 11 import cgi |
12 import copy | 12 import copy |
13 import operator | 13 import operator |
14 import pickle | 14 import pickle |
15 import random | 15 import random |
16 import sys | 16 import sys |
17 import threading | 17 import threading |
18 import time | 18 import time |
19 import urlparse | 19 import urlparse |
(...skipping 10 matching lines...) Expand all Loading... | |
30 import preference_specifics_pb2 | 30 import preference_specifics_pb2 |
31 import search_engine_specifics_pb2 | 31 import search_engine_specifics_pb2 |
32 import session_specifics_pb2 | 32 import session_specifics_pb2 |
33 import sync_pb2 | 33 import sync_pb2 |
34 import sync_enums_pb2 | 34 import sync_enums_pb2 |
35 import theme_specifics_pb2 | 35 import theme_specifics_pb2 |
36 import typed_url_specifics_pb2 | 36 import typed_url_specifics_pb2 |
37 | 37 |
38 # An enumeration of the various kinds of data that can be synced. | 38 # An enumeration of the various kinds of data that can be synced. |
39 # Over the wire, this enumeration is not used: a sync object's type is | 39 # Over the wire, this enumeration is not used: a sync object's type is |
40 # inferred by which EntitySpecifics extension it has. But in the context | 40 # inferred by which EntitySpecifics field it has. But in the context |
41 # of a program, it is useful to have an enumeration. | 41 # of a program, it is useful to have an enumeration. |
42 ALL_TYPES = ( | 42 ALL_TYPES = ( |
43 TOP_LEVEL, # The type of the 'Google Chrome' folder. | 43 TOP_LEVEL, # The type of the 'Google Chrome' folder. |
44 APPS, | 44 APPS, |
45 APP_NOTIFICATION, | 45 APP_NOTIFICATION, |
46 APP_SETTINGS, | 46 APP_SETTINGS, |
47 AUTOFILL, | 47 AUTOFILL, |
48 AUTOFILL_PROFILE, | 48 AUTOFILL_PROFILE, |
49 BOOKMARK, | 49 BOOKMARK, |
50 EXTENSIONS, | 50 EXTENSIONS, |
(...skipping 10 matching lines...) Expand all Loading... | |
61 # to the client. This would be specified by the url that triggers the error. | 61 # to the client. This would be specified by the url that triggers the error. |
62 # Note: This enum should be kept in the same order as the enum in sync_test.h. | 62 # Note: This enum should be kept in the same order as the enum in sync_test.h. |
63 SYNC_ERROR_FREQUENCY = ( | 63 SYNC_ERROR_FREQUENCY = ( |
64 ERROR_FREQUENCY_NONE, | 64 ERROR_FREQUENCY_NONE, |
65 ERROR_FREQUENCY_ALWAYS, | 65 ERROR_FREQUENCY_ALWAYS, |
66 ERROR_FREQUENCY_TWO_THIRDS) = range(3) | 66 ERROR_FREQUENCY_TWO_THIRDS) = range(3) |
67 | 67 |
68 # Well-known server tag of the top level 'Google Chrome' folder. | 68 # Well-known server tag of the top level 'Google Chrome' folder. |
69 TOP_LEVEL_FOLDER_TAG = 'google_chrome' | 69 TOP_LEVEL_FOLDER_TAG = 'google_chrome' |
70 | 70 |
71 # Given a sync type from ALL_TYPES, find the extension token corresponding | 71 # Given a sync type from ALL_TYPES, find the FieldDescriptor corresponding |
72 # to that datatype. Note that TOP_LEVEL has no such token. | 72 # to that datatype. Note that TOP_LEVEL has no such token. |
73 SYNC_TYPE_TO_EXTENSION = { | 73 SYNC_TYPE_FIELDS = sync_pb2.EntitySpecifics.DESCRIPTOR.fields_by_name |
74 APP_NOTIFICATION: app_notification_specifics_pb2.app_notification, | 74 SYNC_TYPE_TO_DESCRIPTOR = { |
75 APP_SETTINGS: app_setting_specifics_pb2.app_setting, | 75 APP_NOTIFICATION: SYNC_TYPE_FIELDS['app_notification'], |
76 APPS: app_specifics_pb2.app, | 76 APP_SETTINGS: SYNC_TYPE_FIELDS['app_setting'], |
77 AUTOFILL: autofill_specifics_pb2.autofill, | 77 APPS: SYNC_TYPE_FIELDS['app'], |
78 AUTOFILL_PROFILE: autofill_specifics_pb2.autofill_profile, | 78 AUTOFILL: SYNC_TYPE_FIELDS['autofill'], |
79 BOOKMARK: bookmark_specifics_pb2.bookmark, | 79 AUTOFILL_PROFILE: SYNC_TYPE_FIELDS['autofill_profile'], |
80 EXTENSION_SETTINGS: extension_setting_specifics_pb2.extension_setting, | 80 BOOKMARK: SYNC_TYPE_FIELDS['bookmark'], |
81 EXTENSIONS: extension_specifics_pb2.extension, | 81 EXTENSION_SETTINGS: SYNC_TYPE_FIELDS['extension_setting'], |
82 NIGORI: nigori_specifics_pb2.nigori, | 82 EXTENSIONS: SYNC_TYPE_FIELDS['extension'], |
83 PASSWORD: password_specifics_pb2.password, | 83 NIGORI: SYNC_TYPE_FIELDS['nigori'], |
84 PREFERENCE: preference_specifics_pb2.preference, | 84 PASSWORD: SYNC_TYPE_FIELDS['password'], |
85 SEARCH_ENGINE: search_engine_specifics_pb2.search_engine, | 85 PREFERENCE: SYNC_TYPE_FIELDS['preference'], |
86 SESSION: session_specifics_pb2.session, | 86 SEARCH_ENGINE: SYNC_TYPE_FIELDS['search_engine'], |
87 THEME: theme_specifics_pb2.theme, | 87 SESSION: SYNC_TYPE_FIELDS['session'], |
88 TYPED_URL: typed_url_specifics_pb2.typed_url, | 88 THEME: SYNC_TYPE_FIELDS['theme'], |
89 TYPED_URL: SYNC_TYPE_FIELDS['typed_url'], | |
89 } | 90 } |
90 | 91 |
91 # The parent ID used to indicate a top-level node. | 92 # The parent ID used to indicate a top-level node. |
92 ROOT_ID = '0' | 93 ROOT_ID = '0' |
93 | 94 |
94 # Unix time epoch in struct_time format. The tuple corresponds to UTC Wednesday | 95 # Unix time epoch in struct_time format. The tuple corresponds to UTC Wednesday |
95 # Jan 1 1970, 00:00:00, non-dst. | 96 # Jan 1 1970, 00:00:00, non-dst. |
96 UNIX_TIME_EPOCH = (1970, 1, 1, 0, 0, 0, 3, 1, 0) | 97 UNIX_TIME_EPOCH = (1970, 1, 1, 0, 0, 0, 3, 1, 0) |
97 | 98 |
98 class Error(Exception): | 99 class Error(Exception): |
99 """Error class for this module.""" | 100 """Error class for this module.""" |
100 | 101 |
101 | 102 |
102 class ProtobufExtensionNotUnique(Error): | 103 class ProtobufDataTypeFieldNotUnique(Error): |
103 """An entry should not have more than one protobuf extension present.""" | 104 """An entry should not have more than one data type present.""" |
104 | 105 |
105 | 106 |
106 class DataTypeIdNotRecognized(Error): | 107 class DataTypeIdNotRecognized(Error): |
107 """The requested data type is not recognized.""" | 108 """The requested data type is not recognized.""" |
108 | 109 |
109 | 110 |
110 class MigrationDoneError(Error): | 111 class MigrationDoneError(Error): |
111 """A server-side migration occurred; clients must re-sync some datatypes. | 112 """A server-side migration occurred; clients must re-sync some datatypes. |
112 | 113 |
113 Attributes: | 114 Attributes: |
(...skipping 22 matching lines...) Expand all Loading... | |
136 | 137 |
137 def GetEntryType(entry): | 138 def GetEntryType(entry): |
138 """Extract the sync type from a SyncEntry. | 139 """Extract the sync type from a SyncEntry. |
139 | 140 |
140 Args: | 141 Args: |
141 entry: A SyncEntity protobuf object whose type to determine. | 142 entry: A SyncEntity protobuf object whose type to determine. |
142 Returns: | 143 Returns: |
143 An enum value from ALL_TYPES if the entry's type can be determined, or None | 144 An enum value from ALL_TYPES if the entry's type can be determined, or None |
144 if the type cannot be determined. | 145 if the type cannot be determined. |
145 Raises: | 146 Raises: |
146 ProtobufExtensionNotUnique: More than one type was indicated by the entry. | 147 ProtobufDataTypeFieldNotUnique: More than one type was indicated by |
148 the entry. | |
147 """ | 149 """ |
148 if entry.server_defined_unique_tag == TOP_LEVEL_FOLDER_TAG: | 150 if entry.server_defined_unique_tag == TOP_LEVEL_FOLDER_TAG: |
149 return TOP_LEVEL | 151 return TOP_LEVEL |
150 entry_types = GetEntryTypesFromSpecifics(entry.specifics) | 152 entry_types = GetEntryTypesFromSpecifics(entry.specifics) |
151 if not entry_types: | 153 if not entry_types: |
152 return None | 154 return None |
153 | 155 |
154 # If there is more than one, either there's a bug, or else the caller | 156 # If there is more than one, either there's a bug, or else the caller |
155 # should use GetEntryTypes. | 157 # should use GetEntryTypes. |
156 if len(entry_types) > 1: | 158 if len(entry_types) > 1: |
157 raise ProtobufExtensionNotUnique | 159 raise ProtobufDataTypeFieldNotUnique |
158 return entry_types[0] | 160 return entry_types[0] |
159 | 161 |
160 | 162 |
161 def GetEntryTypesFromSpecifics(specifics): | 163 def GetEntryTypesFromSpecifics(specifics): |
162 """Determine the sync types indicated by an EntitySpecifics's extension(s). | 164 """Determine the sync types indicated by an EntitySpecifics's field(s). |
163 | 165 |
164 If the specifics have more than one recognized extension (as commonly | 166 If the specifics have more than one recognized data type field (as commonly |
165 happens with the requested_types field of GetUpdatesMessage), all types | 167 happens with the requested_types field of GetUpdatesMessage), all types |
166 will be returned. Callers must handle the possibility of the returned | 168 will be returned. Callers must handle the possibility of the returned |
167 value having more than one item. | 169 value having more than one item. |
168 | 170 |
169 Args: | 171 Args: |
170 specifics: A EntitySpecifics protobuf message whose extensions to | 172 specifics: A EntitySpecifics protobuf message whose extensions to |
171 enumerate. | 173 enumerate. |
172 Returns: | 174 Returns: |
173 A list of the sync types (values from ALL_TYPES) associated with each | 175 A list of the sync types (values from ALL_TYPES) associated with each |
174 recognized extension of the specifics message. | 176 recognized extension of the specifics message. |
175 """ | 177 """ |
176 return [data_type for data_type, extension | 178 return [data_type for data_type, field_descriptor |
177 in SYNC_TYPE_TO_EXTENSION.iteritems() | 179 in SYNC_TYPE_TO_DESCRIPTOR.iteritems() |
178 if specifics.HasExtension(extension)] | 180 if specifics.HasField(field_descriptor.name)] |
179 | 181 |
180 | 182 |
181 def SyncTypeToProtocolDataTypeId(data_type): | 183 def SyncTypeToProtocolDataTypeId(data_type): |
182 """Convert from a sync type (python enum) to the protocol's data type id.""" | 184 """Convert from a sync type (python enum) to the protocol's data type id.""" |
183 return SYNC_TYPE_TO_EXTENSION[data_type].number | 185 return SYNC_TYPE_TO_DESCRIPTOR[data_type].number |
184 | 186 |
185 | 187 |
186 def ProtocolDataTypeIdToSyncType(protocol_data_type_id): | 188 def ProtocolDataTypeIdToSyncType(protocol_data_type_id): |
187 """Convert from the protocol's data type id to a sync type (python enum).""" | 189 """Convert from the protocol's data type id to a sync type (python enum).""" |
188 for data_type, protocol_extension in SYNC_TYPE_TO_EXTENSION.iteritems(): | 190 for data_type, field_descriptor in SYNC_TYPE_TO_DESCRIPTOR.iteritems(): |
189 if protocol_extension.number == protocol_data_type_id: | 191 if field_descriptor.number == protocol_data_type_id: |
190 return data_type | 192 return data_type |
191 raise DataTypeIdNotRecognized | 193 raise DataTypeIdNotRecognized |
192 | 194 |
193 | 195 |
194 def DataTypeStringToSyncTypeLoose(data_type_string): | 196 def DataTypeStringToSyncTypeLoose(data_type_string): |
195 """Converts a human-readable string to a sync type (python enum). | 197 """Converts a human-readable string to a sync type (python enum). |
196 | 198 |
197 Capitalization and pluralization don't matter; this function is appropriate | 199 Capitalization and pluralization don't matter; this function is appropriate |
198 for values that might have been typed by a human being; e.g., command-line | 200 for values that might have been typed by a human being; e.g., command-line |
199 flags or query parameters. | 201 flags or query parameters. |
200 """ | 202 """ |
201 if data_type_string.isdigit(): | 203 if data_type_string.isdigit(): |
202 return ProtocolDataTypeIdToSyncType(int(data_type_string)) | 204 return ProtocolDataTypeIdToSyncType(int(data_type_string)) |
203 name = data_type_string.lower().rstrip('s') | 205 name = data_type_string.lower().rstrip('s') |
204 for data_type, protocol_extension in SYNC_TYPE_TO_EXTENSION.iteritems(): | 206 for data_type, field_descriptor in SYNC_TYPE_TO_DESCRIPTOR.iteritems(): |
205 if protocol_extension.name.lower().rstrip('s') == name: | 207 if field_descriptor.name.lower().rstrip('s') == name: |
206 return data_type | 208 return data_type |
207 raise DataTypeIdNotRecognized | 209 raise DataTypeIdNotRecognized |
208 | 210 |
209 | 211 |
210 def SyncTypeToString(data_type): | 212 def SyncTypeToString(data_type): |
211 """Formats a sync type enum (from ALL_TYPES) to a human-readable string.""" | 213 """Formats a sync type enum (from ALL_TYPES) to a human-readable string.""" |
212 return SYNC_TYPE_TO_EXTENSION[data_type].name | 214 return SYNC_TYPE_TO_DESCRIPTOR[data_type].name |
213 | 215 |
214 | 216 |
215 def CallerInfoToString(caller_info_source): | 217 def CallerInfoToString(caller_info_source): |
216 """Formats a GetUpdatesSource enum value to a readable string.""" | 218 """Formats a GetUpdatesSource enum value to a readable string.""" |
217 return sync_pb2.GetUpdatesCallerInfo.DESCRIPTOR.enum_types_by_name[ | 219 return sync_pb2.GetUpdatesCallerInfo.DESCRIPTOR.enum_types_by_name[ |
218 'GetUpdatesSource'].values_by_number[caller_info_source].name | 220 'GetUpdatesSource'].values_by_number[caller_info_source].name |
219 | 221 |
220 | 222 |
221 def ShortDatatypeListSummary(data_types): | 223 def ShortDatatypeListSummary(data_types): |
222 """Formats compactly a list of sync types (python enums) for human eyes. | 224 """Formats compactly a list of sync types (python enums) for human eyes. |
(...skipping 11 matching lines...) Expand all Loading... | |
234 simple_text = '+'.join(sorted([SyncTypeToString(x) for x in included])) | 236 simple_text = '+'.join(sorted([SyncTypeToString(x) for x in included])) |
235 all_but_text = 'all except %s' % ( | 237 all_but_text = 'all except %s' % ( |
236 '+'.join(sorted([SyncTypeToString(x) for x in excluded]))) | 238 '+'.join(sorted([SyncTypeToString(x) for x in excluded]))) |
237 if len(included) < len(excluded) or len(simple_text) <= len(all_but_text): | 239 if len(included) < len(excluded) or len(simple_text) <= len(all_but_text): |
238 return simple_text | 240 return simple_text |
239 else: | 241 else: |
240 return all_but_text | 242 return all_but_text |
241 | 243 |
242 | 244 |
243 def GetDefaultEntitySpecifics(data_type): | 245 def GetDefaultEntitySpecifics(data_type): |
244 """Get an EntitySpecifics having a sync type's default extension value.""" | 246 """Get an EntitySpecifics having a sync type's default field value.""" |
245 specifics = sync_pb2.EntitySpecifics() | 247 specifics = sync_pb2.EntitySpecifics() |
246 if data_type in SYNC_TYPE_TO_EXTENSION: | 248 if data_type in SYNC_TYPE_TO_DESCRIPTOR: |
247 extension_handle = SYNC_TYPE_TO_EXTENSION[data_type] | 249 descriptor_handle = SYNC_TYPE_TO_DESCRIPTOR[data_type] |
ncarter (slow)
2012/02/28 18:45:09
Drop the "_handle." Just "descriptor." It's cleane
tim (not reviewing)
2012/03/01 22:07:02
Done.
| |
248 specifics.Extensions[extension_handle].SetInParent() | 250 getattr(specifics, descriptor_handle.name).SetInParent() |
249 return specifics | 251 return specifics |
250 | 252 |
251 | 253 |
252 class PermanentItem(object): | 254 class PermanentItem(object): |
253 """A specification of one server-created permanent item. | 255 """A specification of one server-created permanent item. |
254 | 256 |
255 Attributes: | 257 Attributes: |
256 tag: A known-to-the-client value that uniquely identifies a server-created | 258 tag: A known-to-the-client value that uniquely identifies a server-created |
257 permanent item. | 259 permanent item. |
258 name: The human-readable display name for this item. | 260 name: The human-readable display name for this item. |
(...skipping 650 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
909 | 911 |
910 def TriggerSyncTabs(self): | 912 def TriggerSyncTabs(self): |
911 """Set the 'sync_tabs' field to this account's nigori node. | 913 """Set the 'sync_tabs' field to this account's nigori node. |
912 | 914 |
913 If the field is not currently set, will write a new nigori node entry | 915 If the field is not currently set, will write a new nigori node entry |
914 with the field set. Else does nothing. | 916 with the field set. Else does nothing. |
915 """ | 917 """ |
916 | 918 |
917 nigori_tag = "google_chrome_nigori" | 919 nigori_tag = "google_chrome_nigori" |
918 nigori_original = self._entries.get(self._ServerTagToId(nigori_tag)) | 920 nigori_original = self._entries.get(self._ServerTagToId(nigori_tag)) |
919 if (nigori_original.specifics.Extensions[nigori_specifics_pb2.nigori]. | 921 if (nigori_original.specifics.nigori.sync_tabs): |
920 sync_tabs): | |
921 return | 922 return |
922 nigori_new = copy.deepcopy(nigori_original) | 923 nigori_new = copy.deepcopy(nigori_original) |
923 nigori_new.specifics.Extensions[nigori_specifics_pb2.nigori].sync_tabs = ( | 924 nigori_new.specifics.nigori.sync_tabs = True |
924 True) | |
925 self._SaveEntry(nigori_new) | 925 self._SaveEntry(nigori_new) |
926 | 926 |
927 def SetInducedError(self, error, error_frequency, | 927 def SetInducedError(self, error, error_frequency, |
928 sync_count_before_errors): | 928 sync_count_before_errors): |
929 self.induced_error = error | 929 self.induced_error = error |
930 self.induced_error_frequency = error_frequency | 930 self.induced_error_frequency = error_frequency |
931 self.sync_count_before_errors = sync_count_before_errors | 931 self.sync_count_before_errors = sync_count_before_errors |
932 | 932 |
933 def GetInducedError(self): | 933 def GetInducedError(self): |
934 return self.induced_error | 934 return self.induced_error |
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1225 | 1225 |
1226 update_sieve.CheckMigrationState() | 1226 update_sieve.CheckMigrationState() |
1227 | 1227 |
1228 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) | 1228 new_timestamp, entries, remaining = self.account.GetChanges(update_sieve) |
1229 | 1229 |
1230 update_response.changes_remaining = remaining | 1230 update_response.changes_remaining = remaining |
1231 for entry in entries: | 1231 for entry in entries: |
1232 reply = update_response.entries.add() | 1232 reply = update_response.entries.add() |
1233 reply.CopyFrom(entry) | 1233 reply.CopyFrom(entry) |
1234 update_sieve.SaveProgress(new_timestamp, update_response) | 1234 update_sieve.SaveProgress(new_timestamp, update_response) |
OLD | NEW |