• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/bookmarks/bookmark_codec.h"
6 
7 #include <algorithm>
8 
9 #include "base/string_number_conversions.h"
10 #include "base/string_util.h"
11 #include "base/values.h"
12 #include "chrome/browser/bookmarks/bookmark_model.h"
13 #include "googleurl/src/gurl.h"
14 #include "grit/generated_resources.h"
15 #include "ui/base/l10n/l10n_util.h"
16 
17 using base::Time;
18 
19 const char* BookmarkCodec::kRootsKey = "roots";
20 const char* BookmarkCodec::kRootFolderNameKey = "bookmark_bar";
21 const char* BookmarkCodec::kOtherBookmarkFolderNameKey = "other";
22 const char* BookmarkCodec::kVersionKey = "version";
23 const char* BookmarkCodec::kChecksumKey = "checksum";
24 const char* BookmarkCodec::kIdKey = "id";
25 const char* BookmarkCodec::kTypeKey = "type";
26 const char* BookmarkCodec::kNameKey = "name";
27 const char* BookmarkCodec::kDateAddedKey = "date_added";
28 const char* BookmarkCodec::kURLKey = "url";
29 const char* BookmarkCodec::kDateModifiedKey = "date_modified";
30 const char* BookmarkCodec::kChildrenKey = "children";
31 const char* BookmarkCodec::kTypeURL = "url";
32 const char* BookmarkCodec::kTypeFolder = "folder";
33 
34 // Current version of the file.
35 static const int kCurrentVersion = 1;
36 
BookmarkCodec()37 BookmarkCodec::BookmarkCodec()
38     : ids_reassigned_(false),
39       ids_valid_(true),
40       maximum_id_(0) {
41 }
42 
~BookmarkCodec()43 BookmarkCodec::~BookmarkCodec() {}
44 
Encode(BookmarkModel * model)45 Value* BookmarkCodec::Encode(BookmarkModel* model) {
46   return Encode(model->GetBookmarkBarNode(), model->other_node());
47 }
48 
Encode(const BookmarkNode * bookmark_bar_node,const BookmarkNode * other_folder_node)49 Value* BookmarkCodec::Encode(const BookmarkNode* bookmark_bar_node,
50                              const BookmarkNode* other_folder_node) {
51   ids_reassigned_ = false;
52   InitializeChecksum();
53   DictionaryValue* roots = new DictionaryValue();
54   roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node));
55   roots->Set(kOtherBookmarkFolderNameKey, EncodeNode(other_folder_node));
56 
57   DictionaryValue* main = new DictionaryValue();
58   main->SetInteger(kVersionKey, kCurrentVersion);
59   FinalizeChecksum();
60   // We are going to store the computed checksum. So set stored checksum to be
61   // the same as computed checksum.
62   stored_checksum_ = computed_checksum_;
63   main->Set(kChecksumKey, Value::CreateStringValue(computed_checksum_));
64   main->Set(kRootsKey, roots);
65   return main;
66 }
67 
Decode(BookmarkNode * bb_node,BookmarkNode * other_folder_node,int64 * max_id,const Value & value)68 bool BookmarkCodec::Decode(BookmarkNode* bb_node,
69                            BookmarkNode* other_folder_node,
70                            int64* max_id,
71                            const Value& value) {
72   ids_.clear();
73   ids_reassigned_ = false;
74   ids_valid_ = true;
75   maximum_id_ = 0;
76   stored_checksum_.clear();
77   InitializeChecksum();
78   bool success = DecodeHelper(bb_node, other_folder_node, value);
79   FinalizeChecksum();
80   // If either the checksums differ or some IDs were missing/not unique,
81   // reassign IDs.
82   if (!ids_valid_ || computed_checksum() != stored_checksum())
83     ReassignIDs(bb_node, other_folder_node);
84   *max_id = maximum_id_ + 1;
85   return success;
86 }
87 
EncodeNode(const BookmarkNode * node)88 Value* BookmarkCodec::EncodeNode(const BookmarkNode* node) {
89   DictionaryValue* value = new DictionaryValue();
90   std::string id = base::Int64ToString(node->id());
91   value->SetString(kIdKey, id);
92   const string16& title = node->GetTitle();
93   value->SetString(kNameKey, title);
94   value->SetString(kDateAddedKey,
95                    base::Int64ToString(node->date_added().ToInternalValue()));
96   if (node->type() == BookmarkNode::URL) {
97     value->SetString(kTypeKey, kTypeURL);
98     std::string url = node->GetURL().possibly_invalid_spec();
99     value->SetString(kURLKey, url);
100     UpdateChecksumWithUrlNode(id, title, url);
101   } else {
102     value->SetString(kTypeKey, kTypeFolder);
103     value->SetString(kDateModifiedKey,
104                      base::Int64ToString(node->date_folder_modified().
105                                    ToInternalValue()));
106     UpdateChecksumWithFolderNode(id, title);
107 
108     ListValue* child_values = new ListValue();
109     value->Set(kChildrenKey, child_values);
110     for (int i = 0; i < node->child_count(); ++i)
111       child_values->Append(EncodeNode(node->GetChild(i)));
112   }
113   return value;
114 }
115 
DecodeHelper(BookmarkNode * bb_node,BookmarkNode * other_folder_node,const Value & value)116 bool BookmarkCodec::DecodeHelper(BookmarkNode* bb_node,
117                                  BookmarkNode* other_folder_node,
118                                  const Value& value) {
119   if (value.GetType() != Value::TYPE_DICTIONARY)
120     return false;  // Unexpected type.
121 
122   const DictionaryValue& d_value = static_cast<const DictionaryValue&>(value);
123 
124   int version;
125   if (!d_value.GetInteger(kVersionKey, &version) || version != kCurrentVersion)
126     return false;  // Unknown version.
127 
128   Value* checksum_value;
129   if (d_value.Get(kChecksumKey, &checksum_value)) {
130     if (checksum_value->GetType() != Value::TYPE_STRING)
131       return false;
132     StringValue* checksum_value_str = static_cast<StringValue*>(checksum_value);
133     if (!checksum_value_str->GetAsString(&stored_checksum_))
134       return false;
135   }
136 
137   Value* roots;
138   if (!d_value.Get(kRootsKey, &roots))
139     return false;  // No roots.
140 
141   if (roots->GetType() != Value::TYPE_DICTIONARY)
142     return false;  // Invalid type for roots.
143 
144   DictionaryValue* roots_d_value = static_cast<DictionaryValue*>(roots);
145   Value* root_folder_value;
146   Value* other_folder_value;
147   if (!roots_d_value->Get(kRootFolderNameKey, &root_folder_value) ||
148       root_folder_value->GetType() != Value::TYPE_DICTIONARY ||
149       !roots_d_value->Get(kOtherBookmarkFolderNameKey, &other_folder_value) ||
150       other_folder_value->GetType() != Value::TYPE_DICTIONARY)
151     return false;  // Invalid type for root folder and/or other folder.
152 
153   DecodeNode(*static_cast<DictionaryValue*>(root_folder_value), NULL,
154              bb_node);
155   DecodeNode(*static_cast<DictionaryValue*>(other_folder_value), NULL,
156              other_folder_node);
157   // Need to reset the type as decoding resets the type to FOLDER. Similarly
158   // we need to reset the title as the title is persisted and restored from
159   // the file.
160   bb_node->set_type(BookmarkNode::BOOKMARK_BAR);
161   other_folder_node->set_type(BookmarkNode::OTHER_NODE);
162   bb_node->set_title(l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_FOLDER_NAME));
163   other_folder_node->set_title(
164       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
165 
166   return true;
167 }
168 
DecodeChildren(const ListValue & child_value_list,BookmarkNode * parent)169 bool BookmarkCodec::DecodeChildren(const ListValue& child_value_list,
170                                    BookmarkNode* parent) {
171   for (size_t i = 0; i < child_value_list.GetSize(); ++i) {
172     Value* child_value;
173     if (!child_value_list.Get(i, &child_value))
174       return false;
175 
176     if (child_value->GetType() != Value::TYPE_DICTIONARY)
177       return false;
178 
179     DecodeNode(*static_cast<DictionaryValue*>(child_value), parent, NULL);
180   }
181   return true;
182 }
183 
DecodeNode(const DictionaryValue & value,BookmarkNode * parent,BookmarkNode * node)184 bool BookmarkCodec::DecodeNode(const DictionaryValue& value,
185                                BookmarkNode* parent,
186                                BookmarkNode* node) {
187   // If no |node| is specified, we'll create one and add it to the |parent|.
188   // Therefore, in that case, |parent| must be non-NULL.
189   if (!node && !parent) {
190     NOTREACHED();
191     return false;
192   }
193 
194   std::string id_string;
195   int64 id = 0;
196   if (ids_valid_) {
197     if (!value.GetString(kIdKey, &id_string) ||
198         !base::StringToInt64(id_string, &id) ||
199         ids_.count(id) != 0) {
200       ids_valid_ = false;
201     } else {
202       ids_.insert(id);
203     }
204   }
205 
206   maximum_id_ = std::max(maximum_id_, id);
207 
208   string16 title;
209   value.GetString(kNameKey, &title);
210 
211   std::string date_added_string;
212   if (!value.GetString(kDateAddedKey, &date_added_string))
213     date_added_string = base::Int64ToString(Time::Now().ToInternalValue());
214   int64 internal_time;
215   base::StringToInt64(date_added_string, &internal_time);
216   base::Time date_added = base::Time::FromInternalValue(internal_time);
217 #if !defined(OS_WIN)
218   // We changed the epoch for dates on Mac & Linux from 1970 to the Windows
219   // one of 1601. We assume any number we encounter from before 1970 is using
220   // the old format, so we need to add the delta to it.
221   //
222   // This code should be removed at some point:
223   // http://code.google.com/p/chromium/issues/detail?id=20264
224   if (date_added.ToInternalValue() <
225       base::Time::kWindowsEpochDeltaMicroseconds) {
226     date_added = base::Time::FromInternalValue(date_added.ToInternalValue() +
227         base::Time::kWindowsEpochDeltaMicroseconds);
228   }
229 #endif
230 
231   std::string type_string;
232   if (!value.GetString(kTypeKey, &type_string))
233     return false;
234 
235   if (type_string != kTypeURL && type_string != kTypeFolder)
236     return false;  // Unknown type.
237 
238   if (type_string == kTypeURL) {
239     std::string url_string;
240     if (!value.GetString(kURLKey, &url_string))
241       return false;
242 
243     GURL url = GURL(url_string);
244     if (!node && url.is_valid())
245       node = new BookmarkNode(id, url);
246     else
247       return false;  // Node invalid.
248 
249     if (parent)
250       parent->Add(node, parent->child_count());
251     node->set_type(BookmarkNode::URL);
252     UpdateChecksumWithUrlNode(id_string, title, url_string);
253   } else {
254     std::string last_modified_date;
255     if (!value.GetString(kDateModifiedKey, &last_modified_date))
256       last_modified_date = base::Int64ToString(Time::Now().ToInternalValue());
257 
258     Value* child_values;
259     if (!value.Get(kChildrenKey, &child_values))
260       return false;
261 
262     if (child_values->GetType() != Value::TYPE_LIST)
263       return false;
264 
265     if (!node) {
266       node = new BookmarkNode(id, GURL());
267     } else {
268       // If a new node is not created, explicitly assign ID to the existing one.
269       node->set_id(id);
270     }
271 
272     node->set_type(BookmarkNode::FOLDER);
273     int64 internal_time;
274     base::StringToInt64(last_modified_date, &internal_time);
275     node->set_date_folder_modified(Time::FromInternalValue(internal_time));
276 
277     if (parent)
278       parent->Add(node, parent->child_count());
279 
280     UpdateChecksumWithFolderNode(id_string, title);
281 
282     if (!DecodeChildren(*static_cast<ListValue*>(child_values), node))
283       return false;
284   }
285 
286   node->set_title(title);
287   node->set_date_added(date_added);
288   return true;
289 }
290 
ReassignIDs(BookmarkNode * bb_node,BookmarkNode * other_node)291 void BookmarkCodec::ReassignIDs(BookmarkNode* bb_node,
292                                 BookmarkNode* other_node) {
293   maximum_id_ = 0;
294   ReassignIDsHelper(bb_node);
295   ReassignIDsHelper(other_node);
296   ids_reassigned_ = true;
297 }
298 
ReassignIDsHelper(BookmarkNode * node)299 void BookmarkCodec::ReassignIDsHelper(BookmarkNode* node) {
300   DCHECK(node);
301   node->set_id(++maximum_id_);
302   for (int i = 0; i < node->child_count(); ++i)
303     ReassignIDsHelper(node->GetChild(i));
304 }
305 
UpdateChecksum(const std::string & str)306 void BookmarkCodec::UpdateChecksum(const std::string& str) {
307   MD5Update(&md5_context_, str.data(), str.length() * sizeof(char));
308 }
309 
UpdateChecksum(const string16 & str)310 void BookmarkCodec::UpdateChecksum(const string16& str) {
311   MD5Update(&md5_context_, str.data(), str.length() * sizeof(char16));
312 }
313 
UpdateChecksumWithUrlNode(const std::string & id,const string16 & title,const std::string & url)314 void BookmarkCodec::UpdateChecksumWithUrlNode(const std::string& id,
315                                               const string16& title,
316                                               const std::string& url) {
317   DCHECK(IsStringUTF8(url));
318   UpdateChecksum(id);
319   UpdateChecksum(title);
320   UpdateChecksum(kTypeURL);
321   UpdateChecksum(url);
322 }
323 
UpdateChecksumWithFolderNode(const std::string & id,const string16 & title)324 void BookmarkCodec::UpdateChecksumWithFolderNode(const std::string& id,
325                                                  const string16& title) {
326   UpdateChecksum(id);
327   UpdateChecksum(title);
328   UpdateChecksum(kTypeFolder);
329 }
330 
InitializeChecksum()331 void BookmarkCodec::InitializeChecksum() {
332   MD5Init(&md5_context_);
333 }
334 
FinalizeChecksum()335 void BookmarkCodec::FinalizeChecksum() {
336   MD5Digest digest;
337   MD5Final(&digest, &md5_context_);
338   computed_checksum_ = MD5DigestToBase16(digest);
339 }
340