Fcitx
message.cpp
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "../message.h"
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <cerrno>
12 #include <cstdint>
13 #include <memory>
14 #include <stdexcept>
15 #include <string>
16 #include <utility>
17 #include <dbus/dbus-protocol.h>
18 #include <dbus/dbus.h>
19 #include "../../macros.h"
20 #include "../../misc.h"
21 #include "../../unixfd.h"
22 #include "../variant.h"
23 #include "bus_p.h"
24 #include "message_p.h"
25 
26 namespace fcitx::dbus {
27 
28 static char toDBusType(Container::Type type) {
29  char t = '\0';
30  switch (type) {
31  case Container::Type::Array:
32  t = DBUS_TYPE_ARRAY;
33  break;
34  case Container::Type::DictEntry:
35  t = DBUS_TYPE_DICT_ENTRY;
36  break;
37  case Container::Type::Struct:
38  t = DBUS_TYPE_STRUCT;
39  break;
40  case Container::Type::Variant:
41  t = DBUS_TYPE_VARIANT;
42  break;
43  default:
44  throw std::runtime_error("invalid container type");
45  }
46  return t;
47 }
48 
49 Message::Message() : d_ptr(std::make_unique<MessagePrivate>()) {}
50 
51 FCITX_DEFINE_DEFAULT_DTOR_AND_MOVE(Message)
52 
53 Message Message::createReply() const {
54  FCITX_D();
55  auto *dmsg = dbus_message_new_method_return(d->msg());
56  if (!dmsg) {
57  return {};
58  }
59  return MessagePrivate::fromDBusMessage(d->bus_, dmsg, true, false);
60 }
61 
62 Message Message::createError(const char *name, const char *message) const {
63  FCITX_D();
64  auto *dmsg = dbus_message_new_error(d->msg(), name, message);
65  if (!dmsg) {
66  return {};
67  }
68  return MessagePrivate::fromDBusMessage(d->bus_, dmsg, false, false);
69 }
70 
71 MessageType Message::type() const {
72  FCITX_D();
73  return d->type_;
74 }
75 
76 void Message::setDestination(const std::string &dest) {
77  FCITX_D();
78  if (d->msg()) {
79  dbus_message_set_destination(d->msg(), dest.c_str());
80  }
81 }
82 
83 std::string Message::destination() const {
84  FCITX_D();
85  if (!d->msg()) {
86  return {};
87  }
88  const auto *result = dbus_message_get_destination(d->msg());
89  return result ? result : "";
90 }
91 
92 std::string Message::sender() const {
93  FCITX_D();
94  if (!d->msg()) {
95  return {};
96  }
97  const auto *sender = dbus_message_get_sender(d->msg());
98  return sender ? sender : "";
99 }
100 
101 std::string Message::member() const {
102  FCITX_D();
103  if (!d->msg()) {
104  return {};
105  }
106  const auto *member = dbus_message_get_member(d->msg());
107  return member ? member : "";
108 }
109 
110 std::string Message::interface() const {
111  FCITX_D();
112  if (!d->msg()) {
113  return {};
114  }
115  const auto *interface = dbus_message_get_interface(d->msg());
116  return interface ? interface : "";
117 }
118 
119 std::string Message::signature() const {
120  FCITX_D();
121  if (!d->msg()) {
122  return {};
123  }
124  const auto *signature = dbus_message_get_signature(d->msg());
125  return signature ? signature : "";
126 }
127 
128 std::string Message::path() const {
129  FCITX_D();
130  const auto *path = dbus_message_get_path(d->msg());
131  return path ? path : "";
132 }
133 
134 std::string Message::errorName() const {
135  FCITX_D();
136  if (d->msg()) {
137  const auto *err = dbus_message_get_error_name(d->msg());
138  return err ? err : "";
139  }
140  return d->error_;
141 }
142 
143 std::string Message::errorMessage() const {
144  FCITX_D();
145  if (d->msg()) {
146  char *message = nullptr;
147  if (dbus_message_get_args(d->msg(), nullptr, DBUS_TYPE_STRING, &message,
148  DBUS_TYPE_INVALID)) {
149  return message;
150  }
151  return "";
152  }
153  return d->message_;
154 }
155 
156 void *Message::nativeHandle() const {
157  FCITX_D();
158  return d->msg();
159 }
160 
161 int convertTimeout(uint64_t timeout) {
162  int milliTimout = timeout / 1000;
163  if (timeout > 0 && milliTimout == 0) {
164  milliTimout = 1;
165  } else if (timeout == 0) {
166  milliTimout = DBUS_TIMEOUT_USE_DEFAULT;
167  }
168  return milliTimout;
169 }
170 
171 Message Message::call(uint64_t usec) {
172  FCITX_D();
173  ScopedDBusError error;
174  auto *bus = d->bus_.get();
175  if (!bus) {
176  return Message();
177  }
178  DBusMessage *reply = dbus_connection_send_with_reply_and_block(
179  bus->conn_.get(), d->msg(), convertTimeout(usec), &error.error());
180  if (!reply) {
181  return MessagePrivate::fromDBusError(error.error());
182  }
183  return MessagePrivate::fromDBusMessage(bus->watch(), reply, false, false);
184 }
185 
186 void DBusPendingCallNotifyCallback(DBusPendingCall *reply, void *userdata) {
187  auto *slot = static_cast<DBusAsyncCallSlot *>(userdata);
188  if (!slot) {
189  return;
190  }
191 
192  auto msg = MessagePrivate::fromDBusMessage(
193  slot->bus_, dbus_pending_call_steal_reply(reply), false, false);
194  slot->callback_(msg);
195 }
196 
197 std::unique_ptr<Slot> Message::callAsync(uint64_t usec,
198  MessageCallback callback) {
199  FCITX_D();
200  auto *bus = d->bus_.get();
201  if (!bus) {
202  return nullptr;
203  }
204  auto slot = std::make_unique<DBusAsyncCallSlot>(std::move(callback));
205  DBusPendingCall *call = nullptr;
206  if (!dbus_connection_send_with_reply(bus->conn_.get(), d->msg(), &call,
207  convertTimeout(usec))) {
208  return nullptr;
209  }
210 
211  dbus_pending_call_set_notify(call, DBusPendingCallNotifyCallback,
212  slot.get(), nullptr);
213  slot->reply_ = call;
214  slot->bus_ = bus->watch();
215 
216  return slot;
217 }
218 
219 Message::operator bool() const {
220  FCITX_D();
221  return d->msg() && d->lastError_ >= 0;
222 }
223 
224 bool Message::end() const {
225  FCITX_D();
226  return d->end();
227 }
228 
230  FCITX_D();
231  d->lastError_ = 0;
232 }
233 
235  FCITX_D();
236  d->rewind();
237 }
238 
240  FCITX_D();
241  dbus_message_iter_next(d->iterator());
242 }
243 
244 uint64_t Message::serial() {
245  FCITX_D();
246  if (!d->msg()) {
247  return 0;
248  }
249  return dbus_message_get_serial(d->msg());
250 }
251 
252 uint64_t Message::replySerial() {
253  FCITX_D();
254  if (!d->msg()) {
255  return 0;
256  }
257  return dbus_message_get_reply_serial(d->msg());
258 }
259 
260 std::pair<char, std::string> Message::peekType() {
261  FCITX_D();
262 
263  UniqueCPtr<char, dbus_free> content;
264  auto container = dbus_message_iter_get_arg_type(d->iterator());
265  if (container == DBUS_TYPE_ARRAY || container == DBUS_TYPE_STRUCT ||
266  container == DBUS_TYPE_VARIANT) {
267  auto *subIter = d->pushReadIterator();
268  content.reset(dbus_message_iter_get_signature(subIter));
269  d->pop();
270  }
271  if (content) {
272  return {container, content.get()};
273  }
274  return {container, ""};
275 }
276 
278  FCITX_D();
279  auto *bus = d->bus_.get();
280  if (bus) {
281  if (dbus_connection_send(bus->conn_.get(), d->msg(), nullptr)) {
282  return true;
283  }
284  }
285  return false;
286 }
287 
288 Message &Message::operator<<(bool b) {
289  FCITX_D();
290  dbus_bool_t i = b ? 1 : 0;
291  d->lastError_ =
292  dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_BOOLEAN, &i);
293  return *this;
294 }
295 
296 Message &Message::operator>>(bool &b) {
297  FCITX_D();
298  dbus_bool_t i = 0;
299  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_BOOLEAN) {
300  dbus_message_iter_get_basic(d->iterator(), &i);
301  b = i != 0;
302  dbus_message_iter_next(d->iterator());
303  }
304  return *this;
305 }
306 
307 // NOLINTBEGIN(bugprone-macro-parentheses)
308 #define _MARSHALL_FUNC(TYPE, TYPE2) \
309  Message &Message::operator<<(TYPE v) { \
310  if (!(*this)) { \
311  return *this; \
312  } \
313  FCITX_D(); \
314  d->lastError_ = !dbus_message_iter_append_basic( \
315  d->iterator(), DBUS_TYPE_##TYPE2, &v); \
316  return *this; \
317  } \
318  Message &Message::operator>>(TYPE &v) { \
319  if (!(*this)) { \
320  return *this; \
321  } \
322  FCITX_D(); \
323  if (dbus_message_iter_get_arg_type(d->iterator()) == \
324  DBUS_TYPE_##TYPE2) { \
325  dbus_message_iter_get_basic(d->iterator(), &v); \
326  dbus_message_iter_next(d->iterator()); \
327  } else { \
328  d->lastError_ = -EINVAL; \
329  } \
330  return *this; \
331  }
332 // NOLINTEND(bugprone-macro-parentheses)
333 
334 _MARSHALL_FUNC(uint8_t, BYTE)
335 _MARSHALL_FUNC(int16_t, INT16)
336 _MARSHALL_FUNC(uint16_t, UINT16)
337 _MARSHALL_FUNC(int32_t, INT32)
338 _MARSHALL_FUNC(uint32_t, UINT32)
339 _MARSHALL_FUNC(int64_t, INT64)
340 _MARSHALL_FUNC(uint64_t, UINT64)
341 _MARSHALL_FUNC(double, DOUBLE)
342 
343 Message &Message::operator<<(const std::string &s) {
344  *this << s.c_str();
345  return *this;
346 }
347 
348 Message &Message::operator<<(const char *s) {
349  FCITX_D();
350  if (!(*this)) {
351  return *this;
352  }
353  d->lastError_ =
354  !dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_STRING, &s);
355  return *this;
356 }
357 
358 Message &Message::operator>>(std::string &s) {
359  if (!(*this)) {
360  return *this;
361  }
362  FCITX_D();
363  char *p = nullptr;
364 
365  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_STRING) {
366  dbus_message_iter_get_basic(d->iterator(), &p);
367  s = p;
368  dbus_message_iter_next(d->iterator());
369  } else {
370  d->lastError_ = -EINVAL;
371  }
372  return *this;
373 }
374 
375 Message &Message::operator<<(const ObjectPath &o) {
376  if (!(*this)) {
377  return *this;
378  }
379  FCITX_D();
380  const char *c = o.path().c_str();
381  d->lastError_ = !dbus_message_iter_append_basic(d->iterator(),
382  DBUS_TYPE_OBJECT_PATH, &c);
383  return *this;
384 }
385 
386 Message &Message::operator>>(ObjectPath &o) {
387  if (!(*this)) {
388  return *this;
389  }
390  FCITX_D();
391  char *p = nullptr;
392  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_STRING) {
393  dbus_message_iter_get_basic(d->iterator(), &p);
394  o = ObjectPath(p);
395  dbus_message_iter_next(d->iterator());
396  } else {
397  d->lastError_ = -EINVAL;
398  }
399  return *this;
400 }
401 
402 Message &Message::operator<<(const Signature &s) {
403  if (!(*this)) {
404  return *this;
405  }
406  FCITX_D();
407  const char *c = s.sig().c_str();
408  d->lastError_ =
409  !dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_SIGNATURE, &c);
410  return *this;
411 }
412 
413 Message &Message::operator>>(Signature &s) {
414  if (!(*this)) {
415  return *this;
416  }
417  FCITX_D();
418  char *p = nullptr;
419  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_SIGNATURE) {
420  dbus_message_iter_get_basic(d->iterator(), &p);
421  s = Signature(p);
422  dbus_message_iter_next(d->iterator());
423  } else {
424  d->lastError_ = -EINVAL;
425  }
426  return *this;
427 }
428 
429 Message &Message::operator<<(const UnixFD &fd) {
430  if (!(*this)) {
431  return *this;
432  }
433  FCITX_D();
434  int f = fd.fd();
435  d->lastError_ =
436  dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_UNIX_FD, &f);
437  return *this;
438 }
439 
440 Message &Message::operator>>(UnixFD &fd) {
441  if (!(*this)) {
442  return *this;
443  }
444  FCITX_D();
445  int f = -1;
446  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_UNIX_FD) {
447  dbus_message_iter_get_basic(d->iterator(), &f);
448  fd.give(f);
449  dbus_message_iter_next(d->iterator());
450  } else {
451  d->lastError_ = -EINVAL;
452  }
453  return *this;
454 }
455 
456 Message &Message::operator<<(const Container &c) {
457  if (!(*this)) {
458  return *this;
459  }
460  FCITX_D();
461  d->pushWriteIterator(toDBusType(c.type()), c.content().sig());
462  return *this;
463 }
464 
465 Message &Message::operator>>(const Container &c) {
466  if (!(*this)) {
467  return *this;
468  }
469  FCITX_D();
470 
471  if (dbus_message_iter_get_arg_type(d->iterator()) != toDBusType(c.type())) {
472  d->lastError_ = -EINVAL;
473  } else {
474  DBusMessageIter *subIter = d->pushReadIterator();
475  // these two type doesn't support such check.
476  if (c.type() != Container::Type::DictEntry &&
477  c.type() != Container::Type::Struct) {
478  UniqueCPtr<char, dbus_free> content(
479  dbus_message_iter_get_signature(subIter));
480  if (!content || content.get() != c.content().sig()) {
481  d->lastError_ = -EINVAL;
482  }
483  }
484  }
485  return *this;
486 }
487 
488 Message &Message::operator<<(const ContainerEnd & /*unused*/) {
489  if (!(*this)) {
490  return *this;
491  }
492  FCITX_D();
493  d->pop();
494  return *this;
495 }
496 
497 Message &Message::operator>>(const ContainerEnd & /*unused*/) {
498  if (!(*this)) {
499  return *this;
500  }
501  FCITX_D();
502  d->pop();
503  dbus_message_iter_next(d->iterator());
504  return *this;
505 }
506 
507 Message &Message::operator<<(const Variant &v) {
508  if (!(*this)) {
509  return *this;
510  }
511  if (*this << Container(Container::Type::Variant,
512  Signature(v.signature()))) {
513  v.writeToMessage(*this);
514  if (!(*this)) {
515  return *this;
516  }
517  if (*this) {
518  *this << ContainerEnd();
519  }
520  }
521  return *this;
522 }
523 
524 Message &Message::operator>>(Variant &variant) {
525  if (!(*this)) {
526  return *this;
527  }
528  FCITX_D();
529  auto type = peekType();
530  if (type.first != 'v') {
531  d->lastError_ = -EINVAL;
532  return *this;
533  }
534  if (auto helper = lookupVariantType(type.second)) {
535  if (*this >>
536  Container(Container::Type::Variant, Signature(type.second))) {
537  auto data = helper->copy(nullptr);
538  helper->deserialize(*this, data.get());
539  if (*this) {
540  variant.setRawData(std::move(data), std::move(helper));
541  *this >> ContainerEnd();
542  }
543  }
544  } else {
545  dbus_message_iter_next(d->iterator());
546  }
547  return *this;
548 }
549 } // namespace fcitx::dbus
bool send()
Send this message.
Definition: message.cpp:277
Helper type for serialization, should not be used directly.
Definition: message.h:191
Class wrap around the unix fd.
Definition: unixfd.h:22
std::string destination() const
Return the destination of the message.
Definition: message.cpp:83
Basic DBus type of a DBus message.
Definition: message.h:224
std::string path() const
Return the path of the message.
Definition: message.cpp:128
String like type object signature &#39;g&#39;.
Definition: message.h:164
Message createError(const char *name, const char *message) const
Create a error reply to this message.
Definition: message.cpp:62
void rewind()
Rewind the message to the beginning.
Definition: message.cpp:234
Definition: matchrule.h:78
Helper type for serialization, should not be used directly.
Definition: message.h:175
std::string sender() const
Return the sender of the message.
Definition: message.cpp:92
MessageType type() const
Return the message type.
Definition: message.cpp:71
void * nativeHandle() const
Return the low level internal pointer of the message.
Definition: message.cpp:156
std::string member() const
Return the member of the message.
Definition: message.cpp:101
std::pair< char, std::string > peekType()
Check the next type of data in the message.
Definition: message.cpp:260
std::string interface() const
Return the interface of the message.
Definition: message.cpp:110
int fd() const noexcept
Get the internal fd.
Definition: unixfd.cpp:41
void setDestination(const std::string &dest)
Set the destination of the message.
Definition: message.cpp:76
const std::string & signature() const
Return the signature of the data.
Definition: variant.h:139
std::string errorMessage() const
Return the error message of the message.
Definition: message.cpp:143
Message call(uint64_t usec)
Synchronously call a dbus method with a timeout in microseconds.
Definition: message.cpp:171
void skip()
Skip the next data.
Definition: message.cpp:239
Variant type to be used to box or unbox the dbus variant type.
Definition: variant.h:65
String like type object path &#39;o&#39;.
Definition: message.h:151
void give(int fd) noexcept
Set a new FD and transfer the ownership to UnixFD.
Definition: unixfd.cpp:43
std::string signature() const
Return the signature of the message.
Definition: message.cpp:119
bool end() const
Check if message reaches end.
Definition: message.cpp:224
void resetError()
Clear serialization error.
Definition: message.cpp:229
std::unique_ptr< Slot > callAsync(uint64_t usec, MessageCallback callback)
Asynchronously call a dbus method with a timeout in microseconds.
Definition: message.cpp:197
std::string errorName() const
Return the error name of the message.
Definition: message.cpp:134