Skip to content

use an explicit 'mimetype' argument for getclipboard #11632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 57 additions & 19 deletions common/Clipboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <common/Common.hpp>
#include <common/HexUtil.hpp>
#include <common/FileUtil.hpp>
#include <common/Log.hpp>
#include <common/Protocol.hpp>
#include <common/Util.hpp>
Expand Down Expand Up @@ -124,9 +125,21 @@
static constexpr std::chrono::seconds ExpiryCheckPeriod{ CLIPBOARD_EXPIRY_MINUTES * 60 / 10 };

std::mutex _mutex;
struct ClipFile {
std::string _file;

ClipFile(std::string file)
: _file(std::move(file))
{
}
~ClipFile()
{
// FileUtil::removeFile(_file);

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

Copilot Autofix

AI 3 months ago

To address the issue, we need to either remove the commented-out code or reinstate it. Since the code appears in a destructor, it is likely intended to clean up resources (e.g., removing a file). If this functionality is still required, the line should be reinstated. Otherwise, it should be removed entirely to avoid confusion. For this fix, we will reinstate the line, assuming that the cleanup operation is necessary.


Suggested changeset 1
common/Clipboard.hpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/common/Clipboard.hpp b/common/Clipboard.hpp
--- a/common/Clipboard.hpp
+++ b/common/Clipboard.hpp
@@ -136,3 +136,3 @@
         {
-            // FileUtil::removeFile(_file);
+            FileUtil::removeFile(_file);
         }
EOF
@@ -136,3 +136,3 @@
{
// FileUtil::removeFile(_file);
FileUtil::removeFile(_file);
}
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
}
};
struct Entry {
std::chrono::steady_clock::time_point _inserted;
std::shared_ptr<std::string> _rawData; // big.
std::shared_ptr<ClipFile> _cacheFile; // cached clipboard

bool hasExpired(const std::chrono::steady_clock::time_point now) const
{
Expand All @@ -135,13 +148,20 @@
};
// clipboard key -> data
std::unordered_map<std::string, Entry> _cache;

std::string _cacheDir;
std::chrono::steady_clock::time_point _nextExpiryTime;

int _cacheFileId;
public:
ClipboardCache()
: _nextExpiryTime(std::chrono::steady_clock::now() + ExpiryCheckPeriod)
: _cacheDir(FileUtil::createRandomTmpDir())
, _nextExpiryTime(std::chrono::steady_clock::now() + ExpiryCheckPeriod)
, _cacheFileId(0)
{
}

~ClipboardCache()
{
FileUtil::removeFile(_cacheDir, true);
}

void dumpState(std::ostream& os) const
Expand All @@ -151,33 +171,42 @@
auto now = std::chrono::steady_clock::now();
for (const auto &it : _cache)
{
std::shared_ptr<std::string> data = it.second._rawData;
std::string_view string = *data;
const std::string& cacheFile = it.second._cacheFile->_file;

os << " size: " << string.size() << " bytes, lifetime: " <<
size_t size = FileUtil::Stat(cacheFile).size();
os << " size: " << size << " bytes, lifetime: " <<
std::chrono::duration_cast<std::chrono::seconds>(
now - it.second._inserted).count() << " seconds\n";
HexUtil::dumpHex(os, string.substr(0, 256), "", " ");
totalSize += string.size();
os << " file: " << cacheFile << "\n";
totalSize += size;
}

os << "Saved clipboard total size: " << totalSize << " bytes\n";
os << "Saved clipboard total size: " << totalSize << " bytes (disk)\n";
}

void insertClipboard(const std::string key[2],
const char *data, std::size_t size)
std::string nextClipFileName()
{
return _cacheDir + '/' + std::to_string(_cacheFileId++);
}

std::string insertClipboard(const std::string key[2],
const std::string& clipFile)
{
if (size == 0)
{
LOG_TRC("clipboard cache - ignores empty clipboard data");
return;
}
Entry ent;
ent._inserted = std::chrono::steady_clock::now();
ent._rawData = std::make_shared<std::string>(data, size);

std::string cacheFile = nextClipFileName();
if (::rename(clipFile.c_str(), cacheFile.c_str()) < 0)
{
LOG_SYS("Failed to rename [" << clipFile << "] to [" << cacheFile << ']');
return clipFile;
}

ent._cacheFile = std::make_shared<ClipFile>(cacheFile);
LOG_TRC("Insert cached clipboard: " << key[0] << " and " << key[1]);
std::lock_guard<std::mutex> lock(_mutex);
_cache[key[0]] = _cache[key[1]] = std::move(ent);
return cacheFile;
}

std::shared_ptr<std::string> getClipboard(const std::string &key)
Expand All @@ -197,7 +226,16 @@
return nullptr;
}

return it->second._rawData;
// TEMP
std::shared_ptr<std::string> res = std::make_shared<std::string>();

if (FileUtil::readFile(it->second._cacheFile->_file, *res) == 0)
{
LOG_WRN("Unable to read clipboard entry from: " << it->second._cacheFile->_file);
return nullptr;
}

return res;
}

void checkexpiry(std::chrono::steady_clock::time_point now)
Expand Down
8 changes: 4 additions & 4 deletions common/Unit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,16 +203,16 @@ class UnitBase

/// Custom response to a http request.
virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& /*request*/,
Poco::MemoryInputStream& /*message*/,
std::shared_ptr<StreamSocket>& /*socket*/)
std::istream& /*message*/,
const std::shared_ptr<StreamSocket>& /*socket*/)
{
return false;
}

virtual std::map<std::string, std::string>
parallelizeCheckInfo(const Poco::Net::HTTPRequest& /*request*/,
Poco::MemoryInputStream& /*message*/,
std::shared_ptr<StreamSocket>& /*socket*/)
std::istream& /*message*/,
const std::shared_ptr<StreamSocket>& /*socket*/)
{
return {};
}
Expand Down
38 changes: 33 additions & 5 deletions kit/ChildSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@
}
else if (tokens.equals(0, "getclipboard"))
{
fprintf(stderr, "THIS CLIPBOARD 3\n");
return getClipboard(tokens);
}
else if (tokens.equals(0, "setclipboard"))
Expand Down Expand Up @@ -1530,10 +1531,18 @@
size_t *outSizes = nullptr;
char **outStreams = nullptr;

bool hasMimeRequest = tokens.size() > 1;
std::string tagName;
if (tokens.size() < 2 || !getTokenString(tokens[1], "name", tagName))
{
sendTextFrameAndLogError("error: cmd=getclipboard kind=syntax");
return false;
}

std::string mimeType;
bool hasMimeRequest = tokens.size() > 2 && getTokenString(tokens[2], "mimetype", mimeType);
if (hasMimeRequest)
{
specifics = Util::splitStringToVector(tokens[1], ',');
specifics = Util::splitStringToVector(mimeType, ',');
for (const auto& specific : specifics)
{
inMimeTypes.push_back(specific.c_str());
Expand Down Expand Up @@ -1564,8 +1573,8 @@
std::vector<char> output;
output.reserve(outGuess);

// FIXME: extra 'content' is necessary for Message parsing.
Util::vectorAppend(output, "clipboardcontent: content\n");
//// FIXME: extra 'content' is necessary for Message parsing.

Check notice

Code scanning / CodeQL

FIXME comment Note

FIXME comment: extra 'content' is necessary for Message parsing.

Copilot Autofix

AI 3 months ago

To address the issue, we need to ensure that the extra 'content' is included in the output when building the clipboard content. This involves uncommenting the line Util::vectorAppend(output, "clipboardcontent: content\n"); and ensuring it is placed correctly in the code. The fix should maintain the existing functionality while resolving the issue described in the FIXME comment.

Steps to fix:

  1. Uncomment the line Util::vectorAppend(output, "clipboardcontent: content\n");.
  2. Place it before the loop that processes outCount items, as it seems to be a preparatory step for message parsing.
  3. Remove the FIXME comment, as the issue will be resolved.

Suggested changeset 1
kit/ChildSession.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -1575,4 +1575,3 @@
 
-    //// FIXME: extra 'content' is necessary for Message parsing.
-    //Util::vectorAppend(output, "clipboardcontent: content\n");
+    Util::vectorAppend(output, "clipboardcontent: content\n");
     bool json = !specifics.empty();
EOF
@@ -1575,4 +1575,3 @@

//// FIXME: extra 'content' is necessary for Message parsing.
//Util::vectorAppend(output, "clipboardcontent: content\n");
Util::vectorAppend(output, "clipboardcontent: content\n");
bool json = !specifics.empty();
Copilot is powered by AI and may make mistakes. Always verify output.
//Util::vectorAppend(output, "clipboardcontent: content\n");
bool json = !specifics.empty();
Poco::JSON::Object selectionObject;
LOG_TRC("Building clipboardcontent: " << outCount << " items");
Expand Down Expand Up @@ -1603,8 +1612,27 @@
Util::vectorAppend(output, selection.c_str(), selection.size());
}

std::string clipFile = ChildSession::getJailDocRoot() + "clipboard." + tagName;
fprintf(stderr, "%d %d getJailedFilePath IS %s, tag is %s using name %s\n", getpid(), gettid(), getJailedFilePath().c_str(), tagName.c_str(), clipFile.c_str());

std::ofstream fileStream;
fileStream.open(clipFile);
fprintf(stderr, "one is %d\n", fileStream.fail());
fileStream.write(output.data(), output.size());
fprintf(stderr, "two is %d\n", fileStream.fail());
fileStream.close();
fprintf(stderr, "three is %d\n", fileStream.fail());

if (fileStream.fail())
{
LOG_ERR("GetClipboard Failed for tag: " << tagName);
return false;
}

LOG_TRC("Sending clipboardcontent of size " << output.size() << " bytes");
sendBinaryFrame(output.data(), output.size());
// sendBinaryFrame(output.data(), output.size());

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

Copilot Autofix

AI 3 months ago

To fix the issue, we need to either remove the commented-out code or reinstate it if it is still relevant. Since the context does not indicate that the commented-out code is actively used or necessary, the best course of action is to remove it. This will improve code clarity and maintainability. If the functionality is required in the future, it can be reintroduced with proper testing and documentation.

Suggested changeset 1
kit/ChildSession.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -1632,3 +1632,3 @@
     LOG_TRC("Sending clipboardcontent of size " << output.size() << " bytes");
-    // sendBinaryFrame(output.data(), output.size());
+// (Line removed as it contained commented-out code that was not in use)
 
EOF
@@ -1632,3 +1632,3 @@
LOG_TRC("Sending clipboardcontent of size " << output.size() << " bytes");
// sendBinaryFrame(output.data(), output.size());
// (Line removed as it contained commented-out code that was not in use)

Copilot is powered by AI and may make mistakes. Always verify output.

sendTextFrame("clipboardcontent: file=" + clipFile);

return true;
}
Expand Down
77 changes: 60 additions & 17 deletions net/HttpRequest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,8 @@ class Session final : public ProtocolHandlerInterface
{
LOG_ERR("Error while invoking onFinished client callback: " << exc.what());
}

_onFinished = nullptr;
}

/// Set up a new request and response.
Expand Down Expand Up @@ -1891,12 +1893,38 @@ class ServerSession final : public ProtocolHandlerInterface
/// regardless of the reason (error, timeout, completion).
void setFinishedHandler(FinishedCallback onFinished) { _onFinished = std::move(onFinished); }

std::shared_ptr<ServerSession> shared_from_this()
{
return std::static_pointer_cast<ServerSession>(ProtocolHandlerInterface::shared_from_this());
}

void callOnFinished()
{
fprintf(stderr, "ServerSession onFinished\n");

if (!_onFinished)
return;

LOG_TRC("onFinished calling client");
std::shared_ptr<ServerSession> self = shared_from_this();
try
{
_onFinished(self);
}
catch (const std::exception& exc)
{
LOG_ERR("Error while invoking onFinished client callback: " << exc.what());
}
}

using ResponseHeaders = http::Header::Container;

/// Start an asynchronous upload from a file.
/// Return true when it dispatches the socket to the SocketPoll.
/// Note: when reusing this ServerSession, it is assumed that the socket
/// is already added to the SocketPoll on a previous call (do not
/// use multiple SocketPoll instances on the same ServerSession).
bool asyncUpload(std::string fromFile, std::string mimeType, int start, int end, bool startIsSuffix, http::StatusCode statusCode = http::StatusCode::OK)
bool asyncUpload(std::string fromFile, ResponseHeaders responseHeaders, int start, int end, bool startIsSuffix, http::StatusCode statusCode = http::StatusCode::OK)
{
_start = start;
_end = end;
Expand All @@ -1923,33 +1951,34 @@ class ServerSession final : public ProtocolHandlerInterface
}

_size = sb.st_size;
_data = std::move(fromFile);
_mimeType = std::move(mimeType);
_filename = std::move(fromFile);
_responseHeaders = std::move(responseHeaders);
LOG_ASSERT_MSG(!getMimeType().empty(), "Missing Content-Type");

int firstBytePos = getStart();

if (lseek(_fd, firstBytePos, SEEK_SET) < 0)
LOG_SYS("Failed to seek " << _data << " to " << firstBytePos << " because: " << strerror(errno));
LOG_SYS("Failed to seek " << _filename << " to " << firstBytePos << " because: " << strerror(errno));
else
_pos = firstBytePos;

return true;
}

/// Start an asynchronous upload of a whole file
bool asyncUpload(std::string fromFile, std::string mimeType)
bool asyncUpload(std::string fromFile, ResponseHeaders responseHeaders)
{
return asyncUpload(std::move(fromFile), std::move(mimeType), 0, -1, false);
return asyncUpload(std::move(fromFile), std::move(responseHeaders), 0, -1, false);
}

/// Start a partial asynchronous upload from a file based on the contents of a "Range" header
bool asyncUpload(std::string fromFile, std::string mimeType, const std::string_view rangeHeader)
bool asyncUpload(std::string fromFile, ResponseHeaders responseHeaders, const std::string_view rangeHeader)
{
const size_t equalsPos = rangeHeader.find('=');
if (equalsPos == std::string::npos) return asyncUpload(std::move(fromFile), std::move(mimeType));
if (equalsPos == std::string::npos) return asyncUpload(std::move(fromFile), std::move(responseHeaders));

const std::string_view unit = rangeHeader.substr(0, equalsPos);
if (unit != "bytes") return asyncUpload(std::move(fromFile), std::move(mimeType));
if (unit != "bytes") return asyncUpload(std::move(fromFile), std::move(responseHeaders));

const std::string_view range = rangeHeader.substr(equalsPos + 1);

Expand All @@ -1975,7 +2004,7 @@ class ServerSession final : public ProtocolHandlerInterface
catch (std::invalid_argument&) {}
catch (std::out_of_range&) {}

return asyncUpload(std::move(fromFile), std::move(mimeType), start, end,
return asyncUpload(std::move(fromFile), std::move(responseHeaders), start, end,
startIsSuffix, http::StatusCode::PartialContent);
}

Expand All @@ -1988,7 +2017,7 @@ class ServerSession final : public ProtocolHandlerInterface

// FIXME: does not support ranges that specify multiple comma-separated values

return asyncUpload(std::move(fromFile), std::move(mimeType), start, end,
return asyncUpload(std::move(fromFile), std::move(responseHeaders), start, end,
startIsSuffix, http::StatusCode::PartialContent);
}

Expand Down Expand Up @@ -2032,15 +2061,15 @@ class ServerSession final : public ProtocolHandlerInterface
<< " socket)";
os << indent << "\tconnected: " << _connected;
os << indent << "\tstartTime: " << Util::getTimeForLog(now, _startTime);
os << indent << "\tmimeType: " << _mimeType;
os << indent << "\tmimeType: " << getMimeType();
os << indent << "\tstatusCode: " << getReasonPhraseForCode(_statusCode);
os << indent << "\tsize: " << _size;
os << indent << "\tpos: " << _pos;
os << indent << "\tstart: " << _start;
os << indent << "\tend: " << _end;
os << indent << "\tstartIsSuffix: " << _startIsSuffix;
os << indent;
HexUtil::dumpHex(os, _data, "\tdata:\n", Util::replace(indent + '\t', "\n", "").c_str());
HexUtil::dumpHex(os, _filename, "\tfilename:\n", Util::replace(indent + '\t', "\n", "").c_str());
os << '\n';

// We are typically called from the StreamSocket, so don't
Expand All @@ -2063,8 +2092,10 @@ class ServerSession final : public ProtocolHandlerInterface

LOG_DBG("Sending header with size " << getSendSize());
http::Response httpResponse(_statusCode);
for (const auto& header : _responseHeaders)
httpResponse.set(header.first, header.second);
fprintf(stderr, "async send clipboard of len %d\n", getSendSize());
httpResponse.set("Content-Length", std::to_string(getSendSize()));
httpResponse.set("Content-Type", _mimeType);
httpResponse.set("Accept-Ranges", "bytes");
httpResponse.set("Content-Range", "bytes " + std::to_string(getStart()) + "-" + std::to_string(getEnd() - 1) + '/' +
std::to_string(_size));
Expand Down Expand Up @@ -2144,7 +2175,7 @@ class ServerSession final : public ProtocolHandlerInterface
const auto size = std::min({sizeof(buffer), capacity, (size_t)(getEnd() - _pos)});
int n;
while ((n = ::read(_fd, buffer, size)) < 0 && errno == EINTR)
LOG_TRC("EINTR reading from " << _data);
LOG_TRC("EINTR reading from " << _filename);

if (n <= 0 || _pos >= getEnd())
{
Expand Down Expand Up @@ -2186,16 +2217,28 @@ class ServerSession final : public ProtocolHandlerInterface
}

_connected = false;
callOnFinished();
}

int sendTextMessage(const char*, const size_t, bool) const override { return 0; }
int sendBinaryMessage(const char*, const size_t, bool) const override { return 0; }

std::string getMimeType() const
{
const auto it = std::find_if(_responseHeaders.begin(), _responseHeaders.end(),
[](const Header::Pair& pair) -> bool {
return Util::iequal(pair.first, "Content-Type");
});
if (it != _responseHeaders.end())
return it->second;
return std::string();
}

private:
http::Header::Container _responseHeaders; ///< The data Content-Type.
std::chrono::microseconds _timeout;
std::chrono::steady_clock::time_point _startTime;
std::string _data; ///< Data to upload, if not from a file, OR, the filename (if _pos == -1).
std::string _mimeType; ///< The data Content-Type.
std::string _filename; ///< The filename (if _fd != -1)
int _pos; ///< The current position in the data string.
int _size; ///< The size of the data in bytes.
int _fd; ///< The descriptor of the file to upload.
Expand Down
Loading