Home | History | Annotate | Download | only in bookmarks
      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 
     37 BookmarkCodec::BookmarkCodec()
     38     : ids_reassigned_(false),
     39       ids_valid_(true),
     40       maximum_id_(0) {
     41 }
     42 
     43 BookmarkCodec::~BookmarkCodec() {}
     44 
     45 Value* BookmarkCodec::Encode(BookmarkModel* model) {
     46   return Encode(model->GetBookmarkBarNode(), model->other_node());
     47 }
     48 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    306 void BookmarkCodec::UpdateChecksum(const std::string& str) {
    307   MD5Update(&md5_context_, str.data(), str.length() * sizeof(char));
    308 }
    309 
    310 void BookmarkCodec::UpdateChecksum(const string16& str) {
    311   MD5Update(&md5_context_, str.data(), str.length() * sizeof(char16));
    312 }
    313 
    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 
    324 void BookmarkCodec::UpdateChecksumWithFolderNode(const std::string& id,
    325                                                  const string16& title) {
    326   UpdateChecksum(id);
    327   UpdateChecksum(title);
    328   UpdateChecksum(kTypeFolder);
    329 }
    330 
    331 void BookmarkCodec::InitializeChecksum() {
    332   MD5Init(&md5_context_);
    333 }
    334 
    335 void BookmarkCodec::FinalizeChecksum() {
    336   MD5Digest digest;
    337   MD5Final(&digest, &md5_context_);
    338   computed_checksum_ = MD5DigestToBase16(digest);
    339 }
    340