Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
gntp.cpp
Go to the documentation of this file.
1#include <fc/network/gntp.hpp>
3
4#include <fc/log/logger.hpp>
5#include <fc/asio.hpp>
6#include <fc/network/tcp_socket.hpp>
7#include <fc/crypto/sha1.hpp>
10#include <fc/crypto/rand.hpp>
11#include <fc/crypto/hex.hpp>
12
13#include <set>
14#include <boost/lexical_cast.hpp>
15#include <boost/foreach.hpp>
16#include <boost/algorithm/string/case_conv.hpp>
17
18namespace fc
19{
20 namespace detail
21 {
22 static std::string calc_sha1_base32_of_buffer(const std::string& buffer)
23 {
24 sha1::encoder sha1_encoder;
25 sha1_encoder.write(buffer.c_str(), buffer.size());
26 sha1 sha1_result = sha1_encoder.result();
27 string sha1_result_base32 = to_base32((char*)&sha1_result, sizeof(sha1_result));
28 return sha1_result_base32.c_str();
29 }
30
31
33 {
34 public:
35 std::string _icon_bytes;
36 std::string _sha1_hash;
37
38 gntp_icon_impl(const char* buffer, size_t length) :
39 _icon_bytes(buffer, length),
40 _sha1_hash(calc_sha1_base32_of_buffer(_icon_bytes))
41 {
42 }
43 };
44
46 {
47 public:
48 gntp_notifier_impl(const std::string& host_to_notify = "127.0.0.1", uint16_t port = 23053, const std::optional<std::string>& password = std::optional<std::string>());
49
50 // there's no API to change these right now, it will always notify localhost at the default GNTP port
51 std::string hostname;
53 std::optional<std::string> password;
54
55 std::string application_name;
56 gntp_icon_ptr application_icon;
57
58 gntp_notification_type_list notification_types; // list of all notification types we're registered to send
59
60 std::optional<boost::asio::ip::tcp::endpoint> endpoint; // cache the last endpoint we've connected to
61
62 bool connection_failed; // true after we've tried to connect and failed
63 bool is_registered; // true after we've registered
64
65 void send_gntp_message(const std::string& message);
66 };
67
68 gntp_notifier_impl::gntp_notifier_impl(const std::string& host_to_notify /* = "127.0.0.1" */, uint16_t port /* = 23053 */,
69 const std::optional<std::string>& password /* = std::optional<std::string>() */) :
70 hostname(host_to_notify),
71 port(port),
72 password(password),
73 connection_failed(false),
74 is_registered(false)
75 {
76 }
77
78 void gntp_notifier_impl::send_gntp_message(const std::string& message)
79 {
80 std::shared_ptr<boost::asio::ip::tcp::socket> sock(new boost::asio::ip::tcp::socket(asio::default_io_service()));
81
82 bool connected = false;
83 if (endpoint)
84 {
85 // we've successfully connected before, connect to the same endpoint that worked last time
86 try
87 {
88 asio::tcp::connect(*sock, *endpoint);
89 connected = true;
90 }
91 catch (exception& er)
92 {
93 ilog("Failed to connect to GNTP service using an endpoint that previously worked: ${error_report}",
94 ("error_report", er.to_detail_string()));
95 sock->close();
96 // clear the cached endpoint and fall through to the full connection procedure
97 endpoint = std::optional<boost::asio::ip::tcp::endpoint>();
98 }
99 catch (...)
100 {
101 ilog("Failed to connect to GNTP service using an endpoint that previously worked");
102 sock->close();
103 // clear the cached endpoint and fall through to the full connection procedure
104 endpoint = std::optional<boost::asio::ip::tcp::endpoint>();
105 }
106 }
107 if (!connected)
108 {
109 // do the full connection procedure
110 auto eps = asio::tcp::resolve(hostname, boost::lexical_cast<std::string>(port));
111 if (eps.size() == 0)
112 FC_THROW("Unable to resolve host '${host}'", ("host", hostname));
113
114 for (uint32_t i = 0; i < eps.size(); ++i)
115 {
116 try
117 {
118 boost::system::error_code ec;
119 ilog("Attempting to connect to GNTP srvice");
120 asio::tcp::connect(*sock, eps[i]);
121 endpoint = eps[i];
122 connected = true;
123 break;
124 }
125 catch (const exception& er)
126 {
127 ilog("Failed to connect to GNTP service: ${error_reprot}",
128 ("error_report", er.to_detail_string()) );
129 sock->close();
130 }
131 catch (...)
132 {
133 ilog("Failed to connect to GNTP service");
134 sock->close();
135 }
136 }
137 }
138 if (!connected)
139 FC_THROW("Unable to connect to any resolved endpoint for ${host}:${port}",
140 ("host", hostname)("port", port));
141 try
142 {
143 asio::ostream<boost::asio::ip::tcp::socket> write_stream(sock);
144 write_stream.write(message.c_str(), message.size());
145 write_stream.flush();
146 write_stream.close();
147 }
148 catch (exception& er)
149 {
150 FC_RETHROW_EXCEPTION(er, warn, "Caught an exception while sending data to GNTP service");
151 }
152 catch (...)
153 {
154 FC_THROW("Caught an exception while sending data to GNTP service");
155 }
156 }
157 } // end namespace detail
158
159 gntp_icon::gntp_icon(const char* buffer, size_t length) :
160 my(new detail::gntp_icon_impl(buffer, length))
161 {
162 }
163 gntp_icon::~gntp_icon()
164 {
165 }
166
167 gntp_notifier::gntp_notifier(const std::string& host_to_notify /* = "127.0.0.1" */, uint16_t port /* = 23053 */,
168 const std::optional<std::string>& password /* = std::optional<std::string>() */) :
169 my(new detail::gntp_notifier_impl(host_to_notify, port, password))
170 {
171 }
172
173 gntp_notifier::~gntp_notifier()
174 {
175 }
176
177 void gntp_notifier::set_application_name(std::string appName)
178 {
179 my->application_name = appName;
180 }
181 void gntp_notifier::set_application_icon(const gntp_icon_ptr& icon)
182 {
183 my->application_icon = icon;
184 }
185 void gntp_notifier::add_notification_type(const gntp_notification_type& notification_type)
186 {
187 my->notification_types.push_back(notification_type);
188 }
189
190 void gntp_notifier::register_notifications()
191 {
192 // this call will reset any errors
193 my->connection_failed = false;
194 my->is_registered = false;
195
196 std::ostringstream message;
197 std::set<gntp_icon_ptr> icons_used;
198
199 message << "GNTP/1.0 REGISTER NONE\r\n";
200 message << "Application-Name: " << my->application_name << "\r\n";
201 if (my->application_icon)
202 {
203 message << "Application-Icon: x-growl-resource://" << my->application_icon->my->_sha1_hash << "\r\n";
204 icons_used.insert(my->application_icon);
205 }
206
207 message << "Notifications-Count: " << my->notification_types.size() << "\r\n";
208 for (const gntp_notification_type& notification_type : my->notification_types)
209 {
210 message << "\r\n";
211 message << "Notification-Name: " << notification_type.name << "\r\n";
212 if (!notification_type.display_name.empty())
213 message << "Notification-Display-Name: " << notification_type.display_name << "\r\n";
214 if (notification_type.icon)
215 {
216 message << "Notification-Icon: x-growl-resource://" << notification_type.icon->my->_sha1_hash << "\r\n";
217 icons_used.insert(notification_type.icon);
218 }
219 message << "Notification-Enabled: " << (notification_type.enabled ? "True" : "False") << "\r\n";
220 }
221 if (!icons_used.empty())
222 {
223 message << "\r\n";
224 for (const gntp_icon_ptr& icon : icons_used)
225 {
226 message << "Identifier: " << icon->my->_sha1_hash << "\r\n";
227 message << "Length: " << icon->my->_icon_bytes.size() << "\r\n";
228 message << "\r\n";
229 message << icon->my->_icon_bytes;
230 message << "\r\n";
231 }
232 }
233
234 message << "\r\n\r\n";
235 try
236 {
237 my->send_gntp_message(message.str());
238 my->is_registered = true;
239 }
240 catch (const exception&)
241 {
242 my->connection_failed = true;
243 }
244 }
245 gntp_guid gntp_notifier::send_notification(std::string name, std::string title, std::string text,
246 const gntp_icon_ptr& icon, std::optional<gntp_guid> coalescingId /* = std::optional<gntp_guid>() */)
247 {
248 if (my->connection_failed)
249 return gntp_guid();
250 if (!my->is_registered)
251 return gntp_guid();
252
253 gntp_guid notification_id;
254 rand_pseudo_bytes(notification_id.data(), 20);
255
256 std::ostringstream message;
257 message << "GNTP/1.0 NOTIFY NONE";
258 if (my->password)
259 {
260 char salt[16];
261 rand_pseudo_bytes(salt, sizeof(salt));
262 std::string salted_password = *my->password + std::string(salt, 16);
263 sha256 key = sha256::hash(salted_password);
264 sha256 keyhash = sha256::hash(key.data(), 32);
265 message << " SHA256:" << boost::to_upper_copy(to_hex(keyhash.data(), 32)) << "." << boost::to_upper_copy(to_hex(salt, sizeof(salt)));
266 }
267 message << "\r\n";
268 message << "Application-Name: " << my->application_name << "\r\n";
269 message << "Notification-Name: " << name << "\r\n";
270 message << "Notification-ID: " << notification_id.str() << "\r\n";
271 message << "Notification-Coalescing-ID: " << (coalescingId ? coalescingId->str() : notification_id.str()) << "\r\n";
272 message << "Notification-Title: " << title << "\r\n";
273 message << "Notification-Text: " << text << "\r\n";
274 if (icon)
275 message << "Notification-Icon: x-growl-resource://" << icon->my->_sha1_hash << "\r\n";
276
277 if (icon)
278 {
279 message << "\r\n";
280 message << "Identifier: " << icon->my->_sha1_hash << "\r\n";
281 message << "Length: " << icon->my->_icon_bytes.size() << "\r\n";
282 message << "\r\n";
283 message << icon->my->_icon_bytes;
284 message << "\r\n";
285 }
286 message << "\r\n\r\n";
287 my->send_gntp_message(message.str());
288 return notification_id;
289 }
290
291} // namespace fc
std::string name
std::string _sha1_hash
Definition gntp.cpp:36
gntp_icon_impl(const char *buffer, size_t length)
Definition gntp.cpp:38
std::string _icon_bytes
Definition gntp.cpp:35
std::optional< boost::asio::ip::tcp::endpoint > endpoint
Definition gntp.cpp:60
gntp_notifier_impl(const std::string &host_to_notify="127.0.0.1", uint16_t port=23053, const std::optional< std::string > &password=std::optional< std::string >())
Definition gntp.cpp:68
gntp_notification_type_list notification_types
Definition gntp.cpp:58
void send_gntp_message(const std::string &message)
Definition gntp.cpp:78
std::optional< std::string > password
Definition gntp.cpp:53
gntp_icon_ptr application_icon
Definition gntp.cpp:56
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
std::string to_detail_string(log_level ll=log_level::all) const
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
Defines exception's used by fc.
#define FC_THROW( ...)
#define FC_RETHROW_EXCEPTION(ER, LOG_LEVEL, FORMAT,...)
Appends a log_message to the exception ER and rethrows it.
#define ilog(FORMAT,...)
Definition logger.hpp:118
namespace sysio::chain
Definition authority.cpp:3
fc::string to_base32(const std::vector< char > &vec)
Definition base32.cpp:25
void rand_pseudo_bytes(char *buf, int count)
Definition rand.cpp:16
fc::string to_hex(const char *d, uint32_t s)
Definition hex.cpp:17
constexpr const char sha256[]
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126