Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F114736
kazv-io-job.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
kazv-io-job.cpp
View Options
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include
<kazv-defs.hpp>
#include
"kazv-io-job.hpp"
#include
"matrix-room.hpp"
#include
"kazv-log.hpp"
#include
<QObject>
#include
<QPointer>
#include
<QSharedPointer>
#include
<QSaveFile>
#include
<QFile>
#include
<QString>
#include
<QMimeDatabase>
#include
<QCryptographicHash>
#include
<QLoggingCategory>
#include
<KJob>
#include
<KIO/TransferJob>
#include
<nlohmann/json.hpp>
#include
<optional>
#include
<string>
using
namespace
Qt
::
Literals
::
StringLiterals
;
struct
KazvIOBaseJobPrivate
{
QPointer
<
KIO
::
TransferJob
>
job
;
std
::
optional
<
KazvIOBaseJob
::
ErrorCode
>
result
=
std
::
nullopt
;
std
::
optional
<
Kazv
::
AES256CTRDesc
>
aes
=
std
::
nullopt
;
};
KazvIOBaseJob
::
KazvIOBaseJob
(
std
::
optional
<
Kazv
::
AES256CTRDesc
>
aes
,
QObject
*
parent
)
:
QObject
(
parent
)
,
m_d
(
new
KazvIOBaseJobPrivate
)
{
/**
* For unknown reasons, if a new AES256CTRDesc is not constructed here,
* it will cause the program to crash.
*/
if
(
aes
!=
std
::
nullopt
)
{
m_d
->
aes
=
Kazv
::
AES256CTRDesc
{
aes
.
value
().
key
(),
aes
.
value
().
iv
()};
}
// m_d->aes = aes;
connect
(
this
,
&
KazvIOBaseJob
::
jobChanged
,
this
,
&
KazvIOBaseJob
::
connectJob
);
}
KazvIOBaseJob
::~
KazvIOBaseJob
()
=
default
;
float
KazvIOBaseJob::progress
()
{
if
(
m_d
->
job
.
isNull
())
{
return
0
;
}
return
static_cast
<
float
>
(
m_d
->
job
->
percent
())
/
100
;
}
void
KazvIOBaseJob::suspend
()
{
if
(
m_d
->
job
.
isNull
())
{
emitResult
(
KazvError
);
return
;
}
m_d
->
job
->
suspend
();
}
void
KazvIOBaseJob::resume
()
{
if
(
m_d
->
job
.
isNull
())
{
emitResult
(
KazvError
);
return
;
}
m_d
->
job
->
resume
();
}
void
KazvIOBaseJob::cancel
()
{
if
(
m_d
->
job
.
isNull
())
{
emitResult
(
KazvError
);
return
;
}
emitResult
(
UserCancel
);
m_d
->
job
->
kill
();
}
bool
KazvIOBaseJob::isSuspended
()
{
if
(
m_d
->
job
.
isNull
())
{
return
false
;
}
return
m_d
->
job
->
isSuspended
();
}
bool
KazvIOBaseJob::isResulted
()
{
return
m_d
->
result
.
has_value
();
}
KazvIOBaseJob
::
ErrorCode
KazvIOBaseJob::error
()
{
if
(
m_d
->
result
.
has_value
())
{
return
m_d
->
result
.
value
();
}
// Shouldn't call this function before result are emited
return
NoError
;
}
void
KazvIOBaseJob::connectJob
()
{
connect
(
m_d
->
job
,
&
KJob
::
result
,
this
,
[
this
](
KJob
*
job
)
{
if
(
job
->
error
())
{
emitResult
(
KIOError
);
}
});
connect
(
m_d
->
job
,
&
KJob
::
percentChanged
,
this
,
[
this
](
KJob
*
/* job */
,
unsigned
long
/* percent */
)
{
Q_EMIT
progressChanged
();
});
}
QPointer
<
KIO
::
TransferJob
>
KazvIOBaseJob
::
job
()
{
return
m_d
->
job
;
}
void
KazvIOBaseJob
::
setJob
(
QPointer
<
KIO
::
TransferJob
>
job
)
{
m_d
->
job
=
job
;
Q_EMIT
jobChanged
();
}
void
KazvIOBaseJob
::
emitResult
(
ErrorCode
ec
,
QString
data
)
{
if
(
m_d
->
result
.
has_value
())
{
return
;
}
m_d
->
result
=
ec
;
Q_EMIT
result
(
m_d
->
result
.
value
(),
data
);
}
std
::
optional
<
Kazv
::
AES256CTRDesc
>
KazvIOBaseJob
::
aes
()
{
return
m_d
->
aes
;
}
void
KazvIOBaseJob
::
setAes
(
Kazv
::
AES256CTRDesc
aes
)
{
m_d
->
aes
=
aes
;
}
struct
KazvIODownloadJobPrivate
{
QSharedPointer
<
KazvSaveFile
>
file
;
QString
hash
;
};
KazvIODownloadJob
::
KazvIODownloadJob
(
const
QString
&
hash
,
std
::
optional
<
Kazv
::
AES256CTRDesc
>
aes
,
QObject
*
parent
)
:
KazvIOBaseJob
(
aes
,
parent
)
,
m_d
(
new
KazvIODownloadJobPrivate
)
{
m_d
->
hash
=
hash
;
}
KazvIODownloadJob
::
KazvIODownloadJob
(
const
QString
&
fileName
,
const
QUrl
&
serverUrl
,
bool
showProgressBar
,
const
QString
&
hash
,
std
::
optional
<
Kazv
::
AES256CTRDesc
>
aes
,
QObject
*
parent
)
:
KazvIOBaseJob
(
aes
,
parent
)
,
m_d
(
new
KazvIODownloadJobPrivate
)
{
if
(
setFile
(
fileName
))
{
auto
flag
=
showProgressBar
?
KIO
::
DefaultFlags
:
KIO
::
HideProgressInfo
;
setJob
(
KIO
::
get
(
serverUrl
,
KIO
::
NoReload
,
flag
));
}
m_d
->
hash
=
hash
;
}
KazvIODownloadJob
::~
KazvIODownloadJob
()
=
default
;
QString
KazvIODownloadJob::fileName
()
{
if
(
m_d
->
file
.
isNull
())
{
return
QStringLiteral
(
""
);
}
return
m_d
->
file
->
fileName
();
}
void
KazvIODownloadJob::connectJob
()
{
KazvIOBaseJob
::
connectJob
();
connect
(
this
->
job
(),
&
KIO
::
TransferJob
::
data
,
this
,
&
KazvIODownloadJob
::
writeFile
);
connect
(
this
->
job
(),
&
KJob
::
result
,
this
,
&
KazvIODownloadJob
::
closeFile
);
}
bool
KazvIODownloadJob::setFile
(
QString
fileName
)
{
m_d
->
file
.
reset
(
new
KazvSaveFile
(
fileName
,
this
->
aes
()));
if
(
m_d
->
file
->
open
(
QIODevice
::
WriteOnly
))
{
return
true
;
}
emitResult
(
KazvIOBaseJob
::
OpenFileError
);
return
false
;
}
void
KazvIODownloadJob::writeFile
(
KJob
*
job
,
const
QByteArray
&
data
)
{
if
(
m_d
->
file
.
isNull
()
||
!
m_d
->
file
->
isOpen
())
{
emitResult
(
KazvError
);
job
->
kill
();
return
;
}
auto
len
=
m_d
->
file
->
write
(
data
);
if
(
len
==
-1
)
{
KazvIOBaseJob
::
emitResult
(
WriteFileError
);
job
->
kill
();
}
}
void
KazvIODownloadJob::closeFile
(
KJob
*
job
)
{
if
(
m_d
->
file
.
isNull
())
{
emitResult
(
KazvError
);
return
;
}
if
(
job
->
error
())
{
m_d
->
file
->
cancelWriting
();
return
;
}
if
(
!
m_d
->
hash
.
isEmpty
()
&&
QString
::
fromUtf8
(
m_d
->
file
->
hash
())
!=
m_d
->
hash
)
{
emitResult
(
HashError
);
return
;
}
m_d
->
file
->
commit
();
emitResult
(
NoError
);
}
struct
KazvIOUploadJobPrivate
{
QSharedPointer
<
KazvFile
>
file
;
QString
response
;
QSharedPointer
<
MatrixRoom
>
room
;
QString
mimeType
;
QString
mxcUri
;
QString
relType
;
QString
relatedTo
;
};
KazvIOUploadJob
::
KazvIOUploadJob
(
std
::
optional
<
Kazv
::
AES256CTRDesc
>
aes
,
const
QString
&
relType
,
const
QString
&
relatedTo
,
QObject
*
parent
)
:
KazvIOBaseJob
(
aes
,
parent
)
,
m_d
(
new
KazvIOUploadJobPrivate
)
{
m_d
->
relType
=
relType
;
m_d
->
relatedTo
=
relatedTo
;
}
KazvIOUploadJob
::
KazvIOUploadJob
(
const
QString
fileName
,
const
QUrl
serverUrl
,
const
bool
showProgressBar
,
MatrixRoomList
*
roomList
,
const
QString
&
roomId
,
const
QString
token
,
std
::
optional
<
Kazv
::
AES256CTRDesc
>
aes
,
const
QString
&
relType
,
const
QString
&
relatedTo
,
QObject
*
parent
)
:
KazvIOBaseJob
(
aes
,
parent
)
,
m_d
(
new
KazvIOUploadJobPrivate
)
{
m_d
->
relType
=
relType
;
m_d
->
relatedTo
=
relatedTo
;
if
(
setFile
(
fileName
))
{
auto
kazvUploadJob
=
Kazv
::
Api
::
UploadContentJob
(
serverUrl
.
toString
().
toStdString
(),
token
.
toStdString
(),
Kazv
::
FileDesc
(
std
::
string
()));
auto
jobFlag
=
showProgressBar
?
KIO
::
DefaultFlags
:
KIO
::
HideProgressInfo
;
auto
job
=
KIO
::
http_post
(
QUrl
(
QString
::
fromStdString
(
kazvUploadJob
.
url
())),
m_d
->
file
.
data
(),
jobFlag
);
job
->
addMetaData
(
u
"customHTTPHeader"
_s
,
QStringLiteral
(
"Authorization: "
)
.
append
(
QString
::
fromStdString
(
kazvUploadJob
.
requestHeader
()
->
at
(
"Authorization"
))));
job
->
addMetaData
(
u
"PropagateHttpHeader"
_s
,
u
"true"
_s
);
setJob
(
job
);
if
(
roomList
)
{
m_d
->
room
.
reset
(
roomList
->
room
(
roomId
));
}
}
}
KazvIOUploadJob
::~
KazvIOUploadJob
()
=
default
;
QString
KazvIOUploadJob::fileName
()
{
if
(
m_d
->
file
.
isNull
())
{
return
QStringLiteral
(
""
);
}
return
m_d
->
file
->
fileName
();
}
void
KazvIOUploadJob::connectJob
()
{
KazvIOBaseJob
::
connectJob
();
connect
(
this
->
job
(),
&
KIO
::
TransferJob
::
data
,
this
,
[
this
](
KJob
*
/* job */
,
const
QByteArray
&
data
)
{
m_d
->
response
.
append
(
QString
::
fromUtf8
(
data
));
});
connect
(
this
->
job
(),
&
KJob
::
result
,
this
,
&
KazvIOUploadJob
::
handleResult
);
connect
(
this
->
job
(),
&
KIO
::
TransferJob
::
mimeTypeFound
,
this
,
[
this
](
KJob
*
/* job */
,
const
QString
&
mimeType
)
{
m_d
->
mimeType
=
mimeType
;
});
}
void
KazvIOUploadJob::handleResult
(
KJob
*
job
)
{
if
(
m_d
->
file
.
isNull
())
{
emitResult
(
KazvError
);
return
;
}
m_d
->
file
->
close
();
if
(
job
->
error
())
{
return
;
}
nlohmann
::
json
j
;
try
{
j
=
nlohmann
::
json
::
parse
(
m_d
->
response
.
toStdString
());
}
catch
(...)
{
emitResult
(
ResponseError
);
qCWarning
(
kazvLog
)
<<
"Get a invaild response from server."
;
return
;
}
QString
mxcUri
{};
if
(
j
.
contains
(
"content_uri"
)
&&
j
[
"content_uri"
].
is_string
())
{
mxcUri
=
QString
::
fromStdString
(
j
[
"content_uri"
].
template
get
<
std
::
string
>
());
}
else
{
emitResult
(
ResponseError
);
qCWarning
(
kazvLog
)
<<
"There is no
\"
content_uri
\"
field in response."
;
return
;
}
auto
shouldSendMessage
=
!!
m_d
->
room
;
if
(
shouldSendMessage
)
{
auto
aes
=
this
->
aes
();
if
(
aes
.
has_value
())
{
m_d
->
room
->
sendEncryptedFileMessage
(
QUrl
::
fromLocalFile
(
m_d
->
file
->
fileName
()).
fileName
(),
QMimeDatabase
().
mimeTypeForFile
(
m_d
->
file
.
data
()
->
fileName
()).
name
(),
m_d
->
file
->
size
(),
mxcUri
,
QString
::
fromStdString
(
aes
.
value
().
key
()),
QString
::
fromStdString
(
aes
.
value
().
iv
()),
m_d
->
file
->
hash
(),
m_d
->
relType
,
m_d
->
relatedTo
);
}
else
{
m_d
->
room
->
sendMediaFileMessage
(
QUrl
::
fromLocalFile
(
m_d
->
file
->
fileName
()).
fileName
(),
QMimeDatabase
().
mimeTypeForData
(
m_d
->
file
.
data
()).
name
(),
m_d
->
file
->
size
(),
mxcUri
,
m_d
->
relType
,
m_d
->
relatedTo
);
}
}
KazvIOBaseJob
::
emitResult
(
NoError
,
mxcUri
);
}
bool
KazvIOUploadJob::setFile
(
const
QString
fileName
)
{
m_d
->
file
.
reset
(
new
KazvFile
(
fileName
,
this
->
aes
()));
if
(
m_d
->
file
->
open
(
QIODevice
::
ReadOnly
))
{
return
true
;
}
emitResult
(
KazvIOBaseJob
::
OpenFileError
);
return
false
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Nov 26, 2:05 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40361
Default Alt Text
kazv-io-job.cpp (9 KB)
Attached To
Mode
rK kazv
Attached
Detach File
Event Timeline
Log In to Comment