#include "fileinfo.h" #include "fileinfo_p.h" #include namespace Fm { const char gfile_info_query_attribs[] = "standard::*," "unix::*," "time::*," "access::*," "id::filesystem," "metadata::emblems"; FileInfo::FileInfo() { // FIXME: initialize numeric data members } FileInfo::FileInfo(const GFileInfoPtr& inf, const FilePath& parentDirPath) { setFromGFileInfo(inf, parentDirPath); } FileInfo::~FileInfo() { } void FileInfo::setFromGFileInfo(const GObjectPtr& inf, const FilePath& parentDirPath) { dirPath_ = parentDirPath; const char* tmp, *uri; GIcon* gicon; GFileType type; name_ = g_file_info_get_name(inf.get()); dispName_ = g_file_info_get_display_name(inf.get()); size_ = g_file_info_get_size(inf.get()); tmp = g_file_info_get_content_type(inf.get()); if(!tmp) { tmp = "application/octet-stream"; } mimeType_ = MimeType::fromName(tmp); mode_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_MODE); uid_ = gid_ = -1; if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_UNIX_UID)) { uid_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_UID); } if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_UNIX_GID)) { gid_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_GID); } type = g_file_info_get_file_type(inf.get()); if(0 == mode_) { /* if UNIX file mode is not available, compose a fake one. */ switch(type) { case G_FILE_TYPE_REGULAR: mode_ |= S_IFREG; break; case G_FILE_TYPE_DIRECTORY: mode_ |= S_IFDIR; break; case G_FILE_TYPE_SYMBOLIC_LINK: mode_ |= S_IFLNK; break; case G_FILE_TYPE_SHORTCUT: break; case G_FILE_TYPE_MOUNTABLE: break; case G_FILE_TYPE_SPECIAL: if(mode_) { break; } /* if it's a special file but it doesn't have UNIX mode, compose a fake one. */ if(strcmp(tmp, "inode/chardevice") == 0) { mode_ |= S_IFCHR; } else if(strcmp(tmp, "inode/blockdevice") == 0) { mode_ |= S_IFBLK; } else if(strcmp(tmp, "inode/fifo") == 0) { mode_ |= S_IFIFO; } #ifdef S_IFSOCK else if(strcmp(tmp, "inode/socket") == 0) { mode_ |= S_IFSOCK; } #endif break; case G_FILE_TYPE_UNKNOWN: ; } } if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { isAccessible_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_READ); } else /* assume it's accessible */ { isAccessible_ = true; } if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { isWritable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE); } else /* assume it's writable */ { isWritable_ = true; } if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) { isDeletable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE); } else /* assume it's deletable */ { isDeletable_ = true; } /* special handling for symlinks */ if(g_file_info_get_is_symlink(inf.get())) { mode_ &= ~S_IFMT; /* reset type */ mode_ |= S_IFLNK; /* set type to symlink */ goto _file_is_symlink; } isShortcut_ = false; switch(type) { case G_FILE_TYPE_SHORTCUT: isShortcut_ = true; case G_FILE_TYPE_MOUNTABLE: uri = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); if(uri) { if(g_str_has_prefix(uri, "file:///")) { auto filename = CStrPtr{g_filename_from_uri(uri, nullptr, nullptr)}; target_ = filename.get(); } else { target_ = uri; } if(!mimeType_) { mimeType_ = MimeType::guessFromFileName(target_.c_str()); } } /* if the mime-type is not determined or is unknown */ if(G_UNLIKELY(!mimeType_ || mimeType_->isUnknownType())) { /* FIXME: is this appropriate? */ if(type == G_FILE_TYPE_SHORTCUT) { mimeType_ = MimeType::inodeShortcut(); } else { mimeType_ = MimeType::inodeMountPoint(); } } break; case G_FILE_TYPE_DIRECTORY: if(!mimeType_) { mimeType_ = MimeType::inodeDirectory(); } isReadOnly_ = false; /* default is R/W */ if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_READONLY)) { isReadOnly_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_READONLY); } /* directories should be writable to be deleted by user */ if(isReadOnly_ || !isWritable_) { isDeletable_ = false; } break; case G_FILE_TYPE_SYMBOLIC_LINK: _file_is_symlink: uri = g_file_info_get_symlink_target(inf.get()); if(uri) { if(g_str_has_prefix(uri, "file:///")) { auto filename = CStrPtr{g_filename_from_uri(uri, nullptr, nullptr)}; target_ = filename.get(); } else { target_ = uri; } if(!mimeType_) { mimeType_ = MimeType::guessFromFileName(target_.c_str()); } } /* continue with absent mime type */ default: /* G_FILE_TYPE_UNKNOWN G_FILE_TYPE_REGULAR G_FILE_TYPE_SPECIAL */ if(G_UNLIKELY(!mimeType_)) { if(!mimeType_) { mimeType_ = MimeType::guessFromFileName(name_.c_str()); } } } /* if there is a custom folder icon, use it */ if(isNative() && type == G_FILE_TYPE_DIRECTORY) { auto local_path = path().localPath(); auto dot_dir = CStrPtr{g_build_filename(local_path.get(), ".directory", nullptr)}; if(g_file_test(dot_dir.get(), G_FILE_TEST_IS_REGULAR)) { GKeyFile* kf = g_key_file_new(); if(g_key_file_load_from_file(kf, dot_dir.get(), G_KEY_FILE_NONE, nullptr)) { CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)}; if(icon_name) { auto dot_icon = IconInfo::fromName(icon_name.get()); if(dot_icon && dot_icon->isValid()) { icon_ = dot_icon; } } } g_key_file_free(kf); } } if(!icon_) { /* try file-specific icon first */ gicon = g_file_info_get_icon(inf.get()); if(gicon) { icon_ = IconInfo::fromGIcon(gicon); } } #if 0 /* set "locked" icon on unaccesible folder */ else if(!accessible && type == G_FILE_TYPE_DIRECTORY) { icon = g_object_ref(icon_locked_folder); } else { icon = g_object_ref(fm_mime_type_get_icon(mime_type)); } #endif /* if the file has emblems, add them to the icon */ auto emblem_names = g_file_info_get_attribute_stringv(inf.get(), "metadata::emblems"); if(emblem_names) { auto n_emblems = g_strv_length(emblem_names); for(int i = n_emblems - 1; i >= 0; --i) { emblems_.emplace_front(Fm::IconInfo::fromName(emblem_names[i])); } } tmp = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_ID_FILESYSTEM); filesystemId_ = g_intern_string(tmp); mtime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_MODIFIED); atime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_ACCESS); ctime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_CHANGED); isHidden_ = g_file_info_get_is_hidden(inf.get()); isBackup_ = g_file_info_get_is_backup(inf.get()); isNameChangeable_ = true; /* GVFS tends to ignore this attribute */ isIconChangeable_ = isHiddenChangeable_ = false; if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) { isNameChangeable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME); } // special handling for desktop entry files (show the name and icon defined in the desktop entry instead) if(isNative() && G_UNLIKELY(isDesktopEntry())) { auto local_path = path().localPath(); GKeyFile* kf = g_key_file_new(); if(g_key_file_load_from_file(kf, local_path.get(), G_KEY_FILE_NONE, nullptr)) { /* check if type is correct and supported */ CStrPtr type{g_key_file_get_string(kf, "Desktop Entry", "Type", nullptr)}; if(type) { // Type == "Link" if(strcmp(type.get(), G_KEY_FILE_DESKTOP_TYPE_LINK) == 0) { CStrPtr uri{g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, nullptr)}; if(uri) { isShortcut_ = true; target_ = uri.get(); } } } CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)}; if(icon_name) { icon_ = IconInfo::fromName(icon_name.get()); } /* Use title of the desktop entry for display */ CStrPtr displayName{g_key_file_get_locale_string(kf, "Desktop Entry", "Name", nullptr, nullptr)}; if(displayName) { dispName_ = displayName.get(); } /* handle 'Hidden' key to set hidden attribute */ if(!isHidden_) { isHidden_ = g_key_file_get_boolean(kf, "Desktop Entry", "Hidden", nullptr); } } g_key_file_free(kf); } if(!icon_ && mimeType_) icon_ = mimeType_->icon(); #if 0 GFile* _gf = nullptr; GFileAttributeInfoList* list; auto list = g_file_query_settable_attributes(gf, nullptr, nullptr); if(G_LIKELY(list)) { if(g_file_attribute_info_list_lookup(list, G_FILE_ATTRIBUTE_STANDARD_ICON)) { icon_is_changeable = true; } if(g_file_attribute_info_list_lookup(list, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)) { hidden_is_changeable = true; } g_file_attribute_info_list_unref(list); } if(G_UNLIKELY(_gf)) { g_object_unref(_gf); } #endif } void FileInfo::bindCutFiles(const std::shared_ptr& cutFilesHashSet) { cutFilesHashSet_ = cutFilesHashSet; } bool FileInfo::canThumbnail() const { /* We cannot use S_ISREG here as this exclude all symlinks */ if(size_ == 0 || /* don't generate thumbnails for empty files */ !(mode_ & S_IFREG) || isDesktopEntry() || isUnknownType()) { return false; } return true; } /* full path of the file is required by this function */ bool FileInfo::isExecutableType() const { if(isText()) { /* g_content_type_can_be_executable reports text files as executables too */ /* We don't execute remote files nor files in trash */ if(isNative() && (mode_ & (S_IXOTH | S_IXGRP | S_IXUSR))) { /* it has executable bits so lets check shell-bang */ auto pathStr = path().toString(); int fd = open(pathStr.get(), O_RDONLY); if(fd >= 0) { char buf[2]; ssize_t rdlen = read(fd, &buf, 2); close(fd); if(rdlen == 2 && buf[0] == '#' && buf[1] == '!') { return true; } } } return false; } return mimeType_->canBeExecutable(); } bool FileInfoList::isSameType() const { if(!empty()) { auto& item = front(); for(auto it = cbegin() + 1; it != cend(); ++it) { auto& item2 = *it; if(item->mimeType() != item2->mimeType()) { return false; } } } return true; } bool FileInfoList::isSameFilesystem() const { if(!empty()) { auto& item = front(); for(auto it = cbegin() + 1; it != cend(); ++it) { auto& item2 = *it; if(item->filesystemId() != item2->filesystemId()) { return false; } } } return true; } } // namespace Fm