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 }