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 std::pair<char, std::string> Message::peekType() {
245  FCITX_D();
246 
247  UniqueCPtr<char, dbus_free> content;
248  auto container = dbus_message_iter_get_arg_type(d->iterator());
249  if (container == DBUS_TYPE_ARRAY || container == DBUS_TYPE_STRUCT ||
250  container == DBUS_TYPE_VARIANT) {
251  auto *subIter = d->pushReadIterator();
252  content.reset(dbus_message_iter_get_signature(subIter));
253  d->pop();
254  }
255  if (content) {
256  return {container, content.get()};
257  }
258  return {container, ""};
259 }
260 
262  FCITX_D();
263  auto *bus = d->bus_.get();
264  if (bus) {
265  if (dbus_connection_send(bus->conn_.get(), d->msg(), nullptr)) {
266  return true;
267  }
268  }
269  return false;
270 }
271 
272 Message &Message::operator<<(bool b) {
273  FCITX_D();
274  dbus_bool_t i = b ? 1 : 0;
275  d->lastError_ =
276  dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_BOOLEAN, &i);
277  return *this;
278 }
279 
280 Message &Message::operator>>(bool &b) {
281  FCITX_D();
282  dbus_bool_t i = 0;
283  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_BOOLEAN) {
284  dbus_message_iter_get_basic(d->iterator(), &i);
285  b = i != 0;
286  dbus_message_iter_next(d->iterator());
287  }
288  return *this;
289 }
290 
291 // NOLINTBEGIN(bugprone-macro-parentheses)
292 #define _MARSHALL_FUNC(TYPE, TYPE2) \
293  Message &Message::operator<<(TYPE v) { \
294  if (!(*this)) { \
295  return *this; \
296  } \
297  FCITX_D(); \
298  d->lastError_ = !dbus_message_iter_append_basic( \
299  d->iterator(), DBUS_TYPE_##TYPE2, &v); \
300  return *this; \
301  } \
302  Message &Message::operator>>(TYPE &v) { \
303  if (!(*this)) { \
304  return *this; \
305  } \
306  FCITX_D(); \
307  if (dbus_message_iter_get_arg_type(d->iterator()) == \
308  DBUS_TYPE_##TYPE2) { \
309  dbus_message_iter_get_basic(d->iterator(), &v); \
310  dbus_message_iter_next(d->iterator()); \
311  } else { \
312  d->lastError_ = -EINVAL; \
313  } \
314  return *this; \
315  }
316 // NOLINTEND(bugprone-macro-parentheses)
317 
318 _MARSHALL_FUNC(uint8_t, BYTE)
319 _MARSHALL_FUNC(int16_t, INT16)
320 _MARSHALL_FUNC(uint16_t, UINT16)
321 _MARSHALL_FUNC(int32_t, INT32)
322 _MARSHALL_FUNC(uint32_t, UINT32)
323 _MARSHALL_FUNC(int64_t, INT64)
324 _MARSHALL_FUNC(uint64_t, UINT64)
325 _MARSHALL_FUNC(double, DOUBLE)
326 
327 Message &Message::operator<<(const std::string &s) {
328  *this << s.c_str();
329  return *this;
330 }
331 
332 Message &Message::operator<<(const char *s) {
333  FCITX_D();
334  if (!(*this)) {
335  return *this;
336  }
337  d->lastError_ =
338  !dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_STRING, &s);
339  return *this;
340 }
341 
342 Message &Message::operator>>(std::string &s) {
343  if (!(*this)) {
344  return *this;
345  }
346  FCITX_D();
347  char *p = nullptr;
348 
349  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_STRING) {
350  dbus_message_iter_get_basic(d->iterator(), &p);
351  s = p;
352  dbus_message_iter_next(d->iterator());
353  } else {
354  d->lastError_ = -EINVAL;
355  }
356  return *this;
357 }
358 
359 Message &Message::operator<<(const ObjectPath &o) {
360  if (!(*this)) {
361  return *this;
362  }
363  FCITX_D();
364  const char *c = o.path().c_str();
365  d->lastError_ = !dbus_message_iter_append_basic(d->iterator(),
366  DBUS_TYPE_OBJECT_PATH, &c);
367  return *this;
368 }
369 
370 Message &Message::operator>>(ObjectPath &o) {
371  if (!(*this)) {
372  return *this;
373  }
374  FCITX_D();
375  char *p = nullptr;
376  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_STRING) {
377  dbus_message_iter_get_basic(d->iterator(), &p);
378  o = ObjectPath(p);
379  dbus_message_iter_next(d->iterator());
380  } else {
381  d->lastError_ = -EINVAL;
382  }
383  return *this;
384 }
385 
386 Message &Message::operator<<(const Signature &s) {
387  if (!(*this)) {
388  return *this;
389  }
390  FCITX_D();
391  const char *c = s.sig().c_str();
392  d->lastError_ =
393  !dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_SIGNATURE, &c);
394  return *this;
395 }
396 
397 Message &Message::operator>>(Signature &s) {
398  if (!(*this)) {
399  return *this;
400  }
401  FCITX_D();
402  char *p = nullptr;
403  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_SIGNATURE) {
404  dbus_message_iter_get_basic(d->iterator(), &p);
405  s = Signature(p);
406  dbus_message_iter_next(d->iterator());
407  } else {
408  d->lastError_ = -EINVAL;
409  }
410  return *this;
411 }
412 
413 Message &Message::operator<<(const UnixFD &fd) {
414  if (!(*this)) {
415  return *this;
416  }
417  FCITX_D();
418  int f = fd.fd();
419  d->lastError_ =
420  dbus_message_iter_append_basic(d->iterator(), DBUS_TYPE_UNIX_FD, &f);
421  return *this;
422 }
423 
424 Message &Message::operator>>(UnixFD &fd) {
425  if (!(*this)) {
426  return *this;
427  }
428  FCITX_D();
429  int f = -1;
430  if (dbus_message_iter_get_arg_type(d->iterator()) == DBUS_TYPE_UNIX_FD) {
431  dbus_message_iter_get_basic(d->iterator(), &f);
432  fd.give(f);
433  dbus_message_iter_next(d->iterator());
434  } else {
435  d->lastError_ = -EINVAL;
436  }
437  return *this;
438 }
439 
440 Message &Message::operator<<(const Container &c) {
441  if (!(*this)) {
442  return *this;
443  }
444  FCITX_D();
445  d->pushWriteIterator(toDBusType(c.type()), c.content().sig());
446  return *this;
447 }
448 
449 Message &Message::operator>>(const Container &c) {
450  if (!(*this)) {
451  return *this;
452  }
453  FCITX_D();
454 
455  if (dbus_message_iter_get_arg_type(d->iterator()) != toDBusType(c.type())) {
456  d->lastError_ = -EINVAL;
457  } else {
458  DBusMessageIter *subIter = d->pushReadIterator();
459  // these two type doesn't support such check.
460  if (c.type() != Container::Type::DictEntry &&
461  c.type() != Container::Type::Struct) {
462  UniqueCPtr<char, dbus_free> content(
463  dbus_message_iter_get_signature(subIter));
464  if (!content || content.get() != c.content().sig()) {
465  d->lastError_ = -EINVAL;
466  }
467  }
468  }
469  return *this;
470 }
471 
472 Message &Message::operator<<(const ContainerEnd & /*unused*/) {
473  if (!(*this)) {
474  return *this;
475  }
476  FCITX_D();
477  d->pop();
478  return *this;
479 }
480 
481 Message &Message::operator>>(const ContainerEnd & /*unused*/) {
482  if (!(*this)) {
483  return *this;
484  }
485  FCITX_D();
486  d->pop();
487  dbus_message_iter_next(d->iterator());
488  return *this;
489 }
490 
491 Message &Message::operator<<(const Variant &v) {
492  if (!(*this)) {
493  return *this;
494  }
495  if (*this << Container(Container::Type::Variant,
496  Signature(v.signature()))) {
497  v.writeToMessage(*this);
498  if (!(*this)) {
499  return *this;
500  }
501  if (*this) {
502  *this << ContainerEnd();
503  }
504  }
505  return *this;
506 }
507 
508 Message &Message::operator>>(Variant &variant) {
509  if (!(*this)) {
510  return *this;
511  }
512  FCITX_D();
513  auto type = peekType();
514  if (type.first != 'v') {
515  d->lastError_ = -EINVAL;
516  return *this;
517  }
518  if (auto helper = lookupVariantType(type.second)) {
519  if (*this >>
520  Container(Container::Type::Variant, Signature(type.second))) {
521  auto data = helper->copy(nullptr);
522  helper->deserialize(*this, data.get());
523  if (*this) {
524  variant.setRawData(std::move(data), std::move(helper));
525  *this >> ContainerEnd();
526  }
527  }
528  } else {
529  dbus_message_iter_next(d->iterator());
530  }
531  return *this;
532 }
533 } // namespace fcitx::dbus
bool send()
Send this message.
Definition: message.cpp:261
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:244
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