Home | History | Annotate | Download | only in drive
      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
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 
     10 #include "base/logging.h"
     11 #include "base/platform_file.h"
     12 #include "base/time/time.h"
     13 #include "chrome/browser/chromeos/drive/drive.pb.h"
     14 #include "chrome/browser/chromeos/drive/file_system_util.h"
     15 #include "chrome/browser/drive/drive_api_util.h"
     16 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
     17 
     18 namespace drive {
     19 
     20 namespace {
     21 
     22 const char kSharedWithMeLabel[] = "shared-with-me";
     23 
     24 // Checks if |entry| has a label "shared-with-me", which is added to entries
     25 // shared with the user.
     26 bool HasSharedWithMeLabel(const google_apis::ResourceEntry& entry) {
     27   std::vector<std::string>::const_iterator it =
     28       std::find(entry.labels().begin(), entry.labels().end(),
     29                 kSharedWithMeLabel);
     30   return it != entry.labels().end();
     31 }
     32 
     33 }  // namespace
     34 
     35 bool ConvertToResourceEntry(const google_apis::ResourceEntry& input,
     36                             ResourceEntry* output) {
     37   DCHECK(output);
     38 
     39   ResourceEntry converted;
     40 
     41   // For regular files, the 'filename' and 'title' attribute in the metadata
     42   // may be different (e.g. due to rename). To be consistent with the web
     43   // interface and other client to use the 'title' attribute, instead of
     44   // 'filename', as the file name in the local snapshot.
     45   converted.set_title(input.title());
     46   converted.set_base_name(util::NormalizeFileName(converted.title()));
     47   converted.set_resource_id(input.resource_id());
     48 
     49   // Sets parent Resource ID. On drive.google.com, a file can have multiple
     50   // parents or no parent, but we are forcing a tree-shaped structure (i.e. no
     51   // multi-parent or zero-parent entries). Therefore the first found "parent" is
     52   // used for the entry and if the entry has no parent, we assign a special ID
     53   // which represents no-parent entries. Tracked in http://crbug.com/158904.
     54   const google_apis::Link* parent_link =
     55       input.GetLinkByType(google_apis::Link::LINK_PARENT);
     56   if (parent_link) {
     57     converted.set_parent_resource_id(util::ExtractResourceIdFromUrl(
     58         parent_link->href()));
     59   }
     60   // Apply mapping from an empty parent to the special dummy directory.
     61   if (converted.parent_resource_id().empty())
     62     converted.set_parent_resource_id(util::kDriveOtherDirSpecialResourceId);
     63 
     64   converted.set_deleted(input.deleted());
     65   converted.set_shared_with_me(HasSharedWithMeLabel(input));
     66 
     67   PlatformFileInfoProto* file_info = converted.mutable_file_info();
     68 
     69   file_info->set_last_modified(input.updated_time().ToInternalValue());
     70   // If the file has never been viewed (last_viewed_time().is_null() == true),
     71   // then we will set the last_accessed field in the protocol buffer to 0.
     72   file_info->set_last_accessed(input.last_viewed_time().ToInternalValue());
     73   file_info->set_creation_time(input.published_time().ToInternalValue());
     74 
     75   if (input.is_file() || input.is_hosted_document()) {
     76     FileSpecificInfo* file_specific_info =
     77         converted.mutable_file_specific_info();
     78     if (input.is_file()) {
     79       file_info->set_size(input.file_size());
     80       file_specific_info->set_md5(input.file_md5());
     81 
     82       // The resumable-edit-media link should only be present for regular
     83       // files as hosted documents are not uploadable.
     84     } else if (input.is_hosted_document()) {
     85       // Attach .g<something> extension to hosted documents so we can special
     86       // case their handling in UI.
     87       // TODO(satorux): Figure out better way how to pass input info like kind
     88       // to UI through the File API stack.
     89       const std::string document_extension = input.GetHostedDocumentExtension();
     90       file_specific_info->set_document_extension(document_extension);
     91       converted.set_base_name(
     92           util::NormalizeFileName(converted.title() + document_extension));
     93 
     94       // We don't know the size of hosted docs and it does not matter since
     95       // is has no effect on the quota.
     96       file_info->set_size(0);
     97     }
     98     file_info->set_is_directory(false);
     99     file_specific_info->set_content_mime_type(input.content_mime_type());
    100     file_specific_info->set_is_hosted_document(input.is_hosted_document());
    101 
    102     const google_apis::Link* thumbnail_link =
    103         input.GetLinkByType(google_apis::Link::LINK_THUMBNAIL);
    104     if (thumbnail_link)
    105       file_specific_info->set_thumbnail_url(thumbnail_link->href().spec());
    106 
    107     const google_apis::Link* alternate_link =
    108         input.GetLinkByType(google_apis::Link::LINK_ALTERNATE);
    109     if (alternate_link)
    110       file_specific_info->set_alternate_url(alternate_link->href().spec());
    111   } else if (input.is_folder()) {
    112     file_info->set_is_directory(true);
    113   } else {
    114     // There are two cases to reach here.
    115     // * The entry is something that doesn't map into files (i.e. sites).
    116     //   We don't handle these kind of entries hence return false.
    117     // * The entry is un-shared to you by other owner. In that case, we
    118     //   get an entry with only deleted() and resource_id() fields are
    119     //   filled. Since we want to delete such entries locally as well,
    120     //   in that case we need to return true to proceed.
    121     if (!input.deleted())
    122       return false;
    123   }
    124 
    125   output->Swap(&converted);
    126   return true;
    127 }
    128 
    129 void ConvertResourceEntryToPlatformFileInfo(const ResourceEntry& entry,
    130                                             base::PlatformFileInfo* file_info) {
    131   file_info->size = entry.file_info().size();
    132   file_info->is_directory = entry.file_info().is_directory();
    133   file_info->is_symbolic_link = entry.file_info().is_symbolic_link();
    134   file_info->last_modified = base::Time::FromInternalValue(
    135       entry.file_info().last_modified());
    136   file_info->last_accessed = base::Time::FromInternalValue(
    137       entry.file_info().last_accessed());
    138   file_info->creation_time = base::Time::FromInternalValue(
    139       entry.file_info().creation_time());
    140 }
    141 
    142 void SetPlatformFileInfoToResourceEntry(const base::PlatformFileInfo& file_info,
    143                                         ResourceEntry* entry) {
    144   PlatformFileInfoProto* entry_file_info = entry->mutable_file_info();
    145   entry_file_info->set_size(file_info.size);
    146   entry_file_info->set_is_directory(file_info.is_directory);
    147   entry_file_info->set_is_symbolic_link(file_info.is_symbolic_link);
    148   entry_file_info->set_last_modified(file_info.last_modified.ToInternalValue());
    149   entry_file_info->set_last_accessed(file_info.last_accessed.ToInternalValue());
    150   entry_file_info->set_creation_time(file_info.creation_time.ToInternalValue());
    151 }
    152 
    153 }  // namespace drive
    154