Mercurial > irccd
comparison irccd-test/main.cpp @ 620:c79ae2987955
Irccd: create a brand new irccd-test executable, closes #569 @3h
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 21 Dec 2017 21:55:57 +0100 |
parents | |
children | 1afefb4ffcf8 |
comparison
equal
deleted
inserted
replaced
619:a2ece4ed9f5d | 620:c79ae2987955 |
---|---|
1 /* | |
2 * main.cpp -- irccd-test main file | |
3 * | |
4 * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any | |
7 * purpose with or without fee is hereby granted, provided that the above | |
8 * copyright notice and this permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 */ | |
18 | |
19 #include <irccd/sysconfig.hpp> | |
20 | |
21 #include <algorithm> | |
22 #include <functional> | |
23 #include <iostream> | |
24 #include <string> | |
25 #include <unordered_map> | |
26 | |
27 #include <boost/algorithm/string/trim.hpp> | |
28 #include <boost/filesystem/path.hpp> | |
29 | |
30 #if defined(HAVE_LIBEDIT) | |
31 # include <histedit.h> | |
32 #endif | |
33 | |
34 #include <irccd/options.hpp> | |
35 #include <irccd/string_util.hpp> | |
36 | |
37 #include <irccd/daemon/dynlib_plugin.hpp> | |
38 #include <irccd/daemon/irccd.hpp> | |
39 #include <irccd/daemon/plugin_service.hpp> | |
40 #include <irccd/daemon/server_service.hpp> | |
41 | |
42 #include <irccd/test/debug_server.hpp> | |
43 | |
44 #if defined(HAVE_JS) | |
45 # include <irccd/js/directory_jsapi.hpp> | |
46 # include <irccd/js/elapsed_timer_jsapi.hpp> | |
47 # include <irccd/js/file_jsapi.hpp> | |
48 # include <irccd/js/irccd_jsapi.hpp> | |
49 # include <irccd/js/js_plugin.hpp> | |
50 # include <irccd/js/logger_jsapi.hpp> | |
51 # include <irccd/js/plugin_jsapi.hpp> | |
52 # include <irccd/js/server_jsapi.hpp> | |
53 # include <irccd/js/system_jsapi.hpp> | |
54 # include <irccd/js/timer_jsapi.hpp> | |
55 # include <irccd/js/unicode_jsapi.hpp> | |
56 # include <irccd/js/util_jsapi.hpp> | |
57 #endif | |
58 | |
59 namespace irccd { | |
60 | |
61 namespace su = string_util; | |
62 | |
63 namespace { | |
64 | |
65 boost::asio::io_service io; | |
66 | |
67 std::unique_ptr<irccd> daemon; | |
68 std::shared_ptr<plugin> plugin; | |
69 | |
70 void usage() | |
71 { | |
72 std::cerr << "usage: irccd-test [-c config] plugin-name" << std::endl; | |
73 std::exit(1); | |
74 } | |
75 | |
76 std::shared_ptr<server> get_server(std::string name) | |
77 { | |
78 name = boost::algorithm::trim_copy(name); | |
79 | |
80 if (name.empty()) | |
81 name = "test"; | |
82 | |
83 auto s = daemon->servers().get(name); | |
84 | |
85 if (!s) { | |
86 s = std::make_shared<debug_server>(io, std::move(name)); | |
87 daemon->servers().add(s); | |
88 } | |
89 | |
90 return s; | |
91 } | |
92 | |
93 std::string get_arg(const std::vector<std::string>& args, unsigned index) | |
94 { | |
95 if (index >= args.size()) | |
96 return ""; | |
97 | |
98 return args[index]; | |
99 } | |
100 | |
101 /* | |
102 * onCommand server origin channel message | |
103 */ | |
104 void on_command(const std::string& data) | |
105 { | |
106 auto args = su::split(data, " ", 4); | |
107 | |
108 plugin->on_command(*daemon, { | |
109 get_server(get_arg(args, 0)), | |
110 get_arg(args, 1), | |
111 get_arg(args, 2), | |
112 get_arg(args, 3) | |
113 }); | |
114 } | |
115 | |
116 /* | |
117 * onConnect server | |
118 */ | |
119 void on_connect(const std::string& data) | |
120 { | |
121 auto args = su::split(data, " "); | |
122 | |
123 plugin->on_connect(*daemon, {get_server(get_arg(args, 0))}); | |
124 } | |
125 | |
126 /* | |
127 * onInvite server origin channel target | |
128 */ | |
129 void on_invite(const std::string& data) | |
130 { | |
131 auto args = su::split(data, " "); | |
132 | |
133 plugin->on_invite(*daemon, { | |
134 get_server(get_arg(args, 0)), | |
135 get_arg(args, 1), | |
136 get_arg(args, 2), | |
137 get_arg(args, 3), | |
138 }); | |
139 } | |
140 | |
141 /* | |
142 * onJoin server origin channel | |
143 */ | |
144 void on_join(const std::string& data) | |
145 { | |
146 auto args = su::split(data, " "); | |
147 | |
148 plugin->on_join(*daemon, { | |
149 get_server(get_arg(args, 0)), | |
150 get_arg(args, 1), | |
151 get_arg(args, 2) | |
152 }); | |
153 } | |
154 | |
155 /* | |
156 * onKick server origin channel reason | |
157 */ | |
158 void on_kick(const std::string& data) | |
159 { | |
160 auto args = su::split(data, " ", 5); | |
161 | |
162 plugin->on_kick(*daemon, { | |
163 get_server(get_arg(args, 0)), | |
164 get_arg(args, 1), | |
165 get_arg(args, 2), | |
166 get_arg(args, 3), | |
167 get_arg(args, 4), | |
168 }); | |
169 } | |
170 | |
171 /* | |
172 * onLoad | |
173 */ | |
174 void on_load(const std::string&) | |
175 { | |
176 plugin->on_load(*daemon); | |
177 } | |
178 | |
179 /* | |
180 * onMe server origin channel message | |
181 */ | |
182 void on_me(const std::string& data) | |
183 { | |
184 auto args = su::split(data, " ", 4); | |
185 | |
186 plugin->on_me(*daemon, { | |
187 get_server(get_arg(args, 0)), | |
188 get_arg(args, 1), | |
189 get_arg(args, 2), | |
190 get_arg(args, 3) | |
191 }); | |
192 } | |
193 | |
194 /* | |
195 * onMessage server origin channel message | |
196 */ | |
197 void on_message(const std::string& data) | |
198 { | |
199 auto args = su::split(data, " ", 4); | |
200 | |
201 plugin->on_message(*daemon, { | |
202 get_server(get_arg(args, 0)), | |
203 get_arg(args, 1), | |
204 get_arg(args, 2), | |
205 get_arg(args, 3) | |
206 }); | |
207 } | |
208 | |
209 /* | |
210 * onMode server origin channel mode limit user mask | |
211 */ | |
212 void on_mode(const std::string& data) | |
213 { | |
214 auto args = su::split(data, " ", 7); | |
215 | |
216 plugin->on_mode(*daemon, { | |
217 get_server(get_arg(args, 0)), | |
218 get_arg(args, 1), | |
219 get_arg(args, 2), | |
220 get_arg(args, 3), | |
221 get_arg(args, 4), | |
222 get_arg(args, 5), | |
223 get_arg(args, 6), | |
224 }); | |
225 } | |
226 | |
227 /* | |
228 * onNames server channel nick1 nick2 nickN | |
229 */ | |
230 void on_names(const std::string& data) | |
231 { | |
232 auto args = su::split(data, " "); | |
233 | |
234 names_event ev; | |
235 | |
236 ev.server = get_server(get_arg(args, 0)); | |
237 ev.channel = get_arg(args, 1); | |
238 | |
239 if (args.size() >= 3U) | |
240 ev.names.insert(ev.names.begin(), args.begin() + 2, args.end()); | |
241 | |
242 plugin->on_names(*daemon, ev); | |
243 } | |
244 | |
245 /* | |
246 * onNick server origin nickname | |
247 */ | |
248 void on_nick(const std::string& data) | |
249 { | |
250 auto args = su::split(data, " "); | |
251 | |
252 plugin->on_nick(*daemon, { | |
253 get_server(get_arg(args, 0)), | |
254 get_arg(args, 1), | |
255 get_arg(args, 2) | |
256 }); | |
257 } | |
258 | |
259 /* | |
260 * onNotice server origin channel nickname | |
261 */ | |
262 void on_notice(const std::string& data) | |
263 { | |
264 auto args = su::split(data, " ", 4); | |
265 | |
266 plugin->on_notice(*daemon, { | |
267 get_server(get_arg(args, 0)), | |
268 get_arg(args, 1), | |
269 get_arg(args, 2), | |
270 get_arg(args, 3) | |
271 }); | |
272 } | |
273 | |
274 /* | |
275 * onPart server origin channel reason | |
276 */ | |
277 void on_part(const std::string& data) | |
278 { | |
279 auto args = su::split(data, " ", 4); | |
280 | |
281 plugin->on_part(*daemon, { | |
282 get_server(get_arg(args, 0)), | |
283 get_arg(args, 1), | |
284 get_arg(args, 2), | |
285 get_arg(args, 3), | |
286 }); | |
287 } | |
288 | |
289 /* | |
290 * onReload | |
291 */ | |
292 void on_reload(const std::string&) | |
293 { | |
294 plugin->on_reload(*daemon); | |
295 } | |
296 | |
297 /* | |
298 * onTopic server origin channel topic | |
299 */ | |
300 void on_topic(const std::string& data) | |
301 { | |
302 auto args = su::split(data, " ", 4); | |
303 | |
304 plugin->on_topic(*daemon, { | |
305 get_server(get_arg(args, 0)), | |
306 get_arg(args, 1), | |
307 get_arg(args, 2), | |
308 get_arg(args, 3) | |
309 }); | |
310 } | |
311 | |
312 /* | |
313 * onUnload | |
314 */ | |
315 void on_unload(const std::string&) | |
316 { | |
317 plugin->on_unload(*daemon); | |
318 } | |
319 | |
320 /* | |
321 * onWhois server nick user host realname chan1 chan2 chanN | |
322 */ | |
323 void on_whois(const std::string& data) | |
324 { | |
325 auto args = su::split(data, " "); | |
326 | |
327 whois_event ev; | |
328 | |
329 ev.server = get_server(get_arg(args, 0)); | |
330 ev.whois.nick = get_arg(args, 1); | |
331 ev.whois.user = get_arg(args, 2); | |
332 ev.whois.host = get_arg(args, 3); | |
333 ev.whois.realname = get_arg(args, 4); | |
334 | |
335 if (args.size() >= 5) | |
336 ev.whois.channels.insert(ev.whois.channels.begin(), args.begin() + 5, args.end()); | |
337 | |
338 plugin->on_whois(*daemon, ev); | |
339 } | |
340 | |
341 /* | |
342 * Table of user functions. | |
343 */ | |
344 using function = std::function<void (const std::string&)>; | |
345 using functions = std::unordered_map<std::string, function>; | |
346 | |
347 static const functions list{ | |
348 { "onCommand", &(on_command) }, | |
349 { "onConnect", &(on_connect) }, | |
350 { "onInvite", &(on_invite) }, | |
351 { "onJoin", &(on_join) }, | |
352 { "onKick", &(on_kick) }, | |
353 { "onLoad", &(on_load) }, | |
354 { "onMe", &(on_me) }, | |
355 { "onMessage", &(on_message) }, | |
356 { "onMode", &(on_mode) }, | |
357 { "onNames", &(on_names) }, | |
358 { "onNick", &(on_nick) }, | |
359 { "onNotice", &(on_notice) }, | |
360 { "onPart", &(on_part) }, | |
361 { "onReload", &(on_reload) }, | |
362 { "onTopic", &(on_topic) }, | |
363 { "onUnload", &(on_unload) }, | |
364 { "onWhois", &(on_whois) } | |
365 }; | |
366 | |
367 void exec(const std::string& line) | |
368 { | |
369 auto pos = line.find(' '); | |
370 auto it = list.find(line.substr(0, pos)); | |
371 | |
372 if (it != list.end()) | |
373 it->second(pos == std::string::npos ? "" : line.substr(pos + 1)); | |
374 } | |
375 | |
376 #if defined(HAVE_LIBEDIT) | |
377 | |
378 const char* prompt(EditLine*) | |
379 { | |
380 static const char* text = "> "; | |
381 | |
382 return text; | |
383 } | |
384 | |
385 std::string clean(std::string input) | |
386 { | |
387 while (!input.empty() && (input.back() == '\n' || input.back() == '\r')) | |
388 input.pop_back(); | |
389 | |
390 return input; | |
391 } | |
392 | |
393 std::vector<std::string> matches(const std::string& name) | |
394 { | |
395 std::vector<std::string> result; | |
396 | |
397 for (const auto& pair : list) | |
398 if (pair.first.compare(0U, name.size(), name) == 0U) | |
399 result.push_back(pair.first); | |
400 | |
401 return result; | |
402 } | |
403 | |
404 unsigned char complete(EditLine* el, int) | |
405 { | |
406 const auto* lf = el_line(el); | |
407 const auto args = su::split(std::string(lf->buffer, lf->cursor), " "); | |
408 | |
409 if (args.size() == 0U) | |
410 return CC_REFRESH; | |
411 | |
412 const auto found = matches(args[0]); | |
413 | |
414 if (found.size() != 1U) | |
415 return CC_REFRESH; | |
416 | |
417 // Insert the missing text, e.g. onCom -> onCommand. | |
418 if (el_insertstr(el, &found[0].c_str()[args[0].size()]) < 0) | |
419 return CC_ERROR; | |
420 | |
421 return CC_REFRESH; | |
422 } | |
423 | |
424 void run() | |
425 { | |
426 std::unique_ptr<EditLine, void (*)(EditLine*)> el( | |
427 el_init("irccd-test", stdin, stdout, stderr), | |
428 el_end | |
429 ); | |
430 std::unique_ptr<History, void (*)(History*)> hist( | |
431 history_init(), | |
432 history_end | |
433 ); | |
434 HistEvent hev; | |
435 | |
436 history(hist.get(), &hev, H_SETSIZE, 1024); | |
437 el_set(el.get(), EL_EDITOR, "emacs"); | |
438 el_set(el.get(), EL_PROMPT, prompt); | |
439 el_set(el.get(), EL_HIST, history, hist.get()); | |
440 el_set(el.get(), EL_ADDFN, "ed-complete", "Complete command", complete); | |
441 el_set(el.get(), EL_BIND, "^I", "ed-complete", nullptr); | |
442 | |
443 const char* s; | |
444 int size; | |
445 | |
446 while ((s = el_gets(el.get(), &size)) && size >= 0) { | |
447 if (size > 0) | |
448 history(hist.get(), &hev, H_ENTER, s); | |
449 | |
450 exec(clean(s)); | |
451 } | |
452 } | |
453 | |
454 #else | |
455 | |
456 void run() | |
457 { | |
458 std::string line; | |
459 | |
460 for (;;) { | |
461 std::cout << "> "; | |
462 std::getline(std::cin, line); | |
463 exec(line); | |
464 } | |
465 } | |
466 | |
467 #endif | |
468 | |
469 void load_plugins(int argc, char** argv) | |
470 { | |
471 if (argc <= 0) | |
472 usage(); | |
473 | |
474 daemon->plugins().load("test", boost::filesystem::exists(argv[0]) ? argv[0] : ""); | |
475 plugin = daemon->plugins().get("test"); | |
476 } | |
477 | |
478 void load_options(int& argc, char**& argv) | |
479 { | |
480 const option::options def{ | |
481 { "-c", true }, | |
482 { "--config", true } | |
483 }; | |
484 | |
485 auto result = option::read(argc, argv, def); | |
486 auto it = result.find("-c"); | |
487 | |
488 if (it == result.end()) | |
489 it = result.find("--config"); | |
490 if (it != result.end()) { | |
491 try { | |
492 daemon->set_config(it->second); | |
493 } catch (const std::exception& ex) { | |
494 throw std::runtime_error(su::sprintf("%s: %s", it->second, ex.what())); | |
495 } | |
496 } | |
497 } | |
498 | |
499 void load(int argc, char** argv) | |
500 { | |
501 daemon = std::make_unique<irccd>(io); | |
502 | |
503 #if defined(HAVE_JS) | |
504 auto loader = std::make_unique<js_plugin_loader>(*daemon); | |
505 | |
506 loader->modules().push_back(std::make_unique<irccd_jsapi>()); | |
507 loader->modules().push_back(std::make_unique<directory_jsapi>()); | |
508 loader->modules().push_back(std::make_unique<elapsed_timer_jsapi>()); | |
509 loader->modules().push_back(std::make_unique<file_jsapi>()); | |
510 loader->modules().push_back(std::make_unique<logger_jsapi>()); | |
511 loader->modules().push_back(std::make_unique<plugin_jsapi>()); | |
512 loader->modules().push_back(std::make_unique<server_jsapi>()); | |
513 loader->modules().push_back(std::make_unique<system_jsapi>()); | |
514 loader->modules().push_back(std::make_unique<timer_jsapi>()); | |
515 loader->modules().push_back(std::make_unique<unicode_jsapi>()); | |
516 loader->modules().push_back(std::make_unique<util_jsapi>()); | |
517 | |
518 daemon->plugins().add_loader(std::move(loader)); | |
519 #endif | |
520 | |
521 load_options(argc, argv); | |
522 load_plugins(argc, argv); | |
523 } | |
524 | |
525 } // !namespace | |
526 | |
527 } // !irccd | |
528 | |
529 int main(int argc, char** argv) | |
530 { | |
531 try { | |
532 irccd::load(--argc, ++argv); | |
533 irccd::run(); | |
534 } catch (const std::exception& ex) { | |
535 std::cerr << "abort: " << ex.what() << std::endl; | |
536 return 1; | |
537 } | |
538 } |