aboutsummaryrefslogtreecommitdiffhomepage
path: root/td/telegram/files/FileHashUploader.cpp
blob: 5357bfaf38d3f9ab73a545f41072b7583ae1b7b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/files/FileHashUploader.h"

#include "td/telegram/files/FileType.h"
#include "td/telegram/Global.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/telegram_api.h"

#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/logging.h"
#include "td/utils/MimeType.h"
#include "td/utils/misc.h"
#include "td/utils/PathView.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/port/PollFlags.h"
#include "td/utils/Status.h"

namespace td {

void FileHashUploader::start_up() {
  auto status = init();
  if (status.is_error()) {
    callback_->on_error(std::move(status));
    stop_flag_ = true;
    return;
  }
}

Status FileHashUploader::init() {
  TRY_RESULT(fd, FileFd::open(local_.path_, FileFd::Read));
  TRY_RESULT(file_size, fd.get_size());
  if (file_size != size_) {
    return Status::Error("Size mismatch");
  }
  fd_ = BufferedFd<FileFd>(std::move(fd));
  sha256_state_.init();

  resource_state_.set_unit_size(1024);
  resource_state_.update_estimated_limit(size_);
  return Status::OK();
}

void FileHashUploader::loop() {
  if (stop_flag_) {
    return;
  }

  auto status = loop_impl();
  if (status.is_error()) {
    callback_->on_error(std::move(status));
    stop_flag_ = true;
    return;
  }
}

Status FileHashUploader::loop_impl() {
  if (state_ == State::CalcSha) {
    TRY_STATUS(loop_sha());
  }
  if (state_ == State::NetRequest) {
    // messages.getDocumentByHash#338e2464 sha256:bytes size:long mime_type:string = Document;
    auto hash = BufferSlice(32);
    sha256_state_.extract(hash.as_mutable_slice(), true);
    auto mime_type = MimeType::from_extension(PathView(local_.path_).extension(), "image/gif");
    auto query = telegram_api::messages_getDocumentByHash(std::move(hash), size_, std::move(mime_type));
    LOG(INFO) << "Send getDocumentByHash request: " << to_string(query);
    auto ptr = G()->net_query_creator().create(query);
    G()->net_query_dispatcher().dispatch_with_callback(std::move(ptr), actor_shared(this));
    state_ = State::WaitNetResult;
  }
  return Status::OK();
}

Status FileHashUploader::loop_sha() {
  auto limit = resource_state_.unused();
  if (limit == 0) {
    return Status::OK();
  }
  if (limit > size_left_) {
    limit = size_left_;
  }
  resource_state_.start_use(limit);

  fd_.get_poll_info().add_flags(PollFlags::Read());
  TRY_RESULT(read_size, fd_.flush_read(static_cast<size_t>(limit)));
  if (read_size != static_cast<size_t>(limit)) {
    return Status::Error("Unexpected end of file");
  }
  while (true) {
    auto ready = fd_.input_buffer().prepare_read();
    if (ready.empty()) {
      break;
    }
    sha256_state_.feed(ready);
    fd_.input_buffer().confirm_read(ready.size());
  }
  resource_state_.stop_use(limit);

  size_left_ -= narrow_cast<int64>(read_size);
  CHECK(size_left_ >= 0);
  if (size_left_ == 0) {
    state_ = State::NetRequest;
    return Status::OK();
  }
  return Status::OK();
}

void FileHashUploader::on_result(NetQueryPtr net_query) {
  auto status = on_result_impl(std::move(net_query));
  if (status.is_error()) {
    callback_->on_error(std::move(status));
    stop_flag_ = true;
    return;
  }
}

Status FileHashUploader::on_result_impl(NetQueryPtr net_query) {
  if (net_query->is_error()) {
    return net_query->move_as_error();
  }
  TRY_RESULT(res, fetch_result<telegram_api::messages_getDocumentByHash>(net_query->ok()));

  switch (res->get_id()) {
    case telegram_api::documentEmpty::ID:
      return Status::Error("Document is not found by hash");
    case telegram_api::document::ID: {
      auto document = move_tl_object_as<telegram_api::document>(res);
      if (!DcId::is_valid(document->dc_id_)) {
        return Status::Error("Found document has invalid DcId");
      }
      callback_->on_ok(FullRemoteFileLocation(FileType::Document, document->id_, document->access_hash_,
                                              DcId::internal(document->dc_id_),
                                              document->file_reference_.as_slice().str()));

      stop_flag_ = true;
      return Status::OK();
    }
    default:
      UNREACHABLE();
      return Status::Error("UNREACHABLE");
  }
}

}  // namespace td