Advertisement
Guest User

Untitled

a guest
Mar 31st, 2020
210
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 38.32 KB | None | 0 0
  1. /*
  2. ============================================================================
  3. Name : minitox.c
  4. Author : tb
  5. Version :
  6. Copyright : Your copyright notice
  7. Description : Hello World in C, Ansi-style
  8. ============================================================================
  9. */
  10.  
  11. /*
  12. * MiniTox - A minimal client for Tox
  13. */
  14.  
  15. #ifndef _POSIX_C_SOURCE
  16. #define _POSIX_C_SOURCE 200809L
  17. #endif
  18.  
  19. #include <stdio.h>
  20. #include <stdint.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <time.h>
  24. #include <stdarg.h>
  25.  
  26. #include <termios.h>
  27. #include <unistd.h>
  28. #include <fcntl.h>
  29.  
  30. #include <tox/tox.h>
  31.  
  32. /*******************************************************************************
  33. *
  34. * Consts & Macros
  35. *
  36. ******************************************************************************/
  37.  
  38. // where to save the tox data.
  39. // if don't want to save, set it to NULL.
  40. const char *savedata_filename = "./savedata.tox";
  41. const char *savedata_tmp_filename = "./savedata.tox.tmp";
  42.  
  43. struct DHT_node {
  44. const char *ip;
  45. uint16_t port;
  46. const char key_hex[TOX_PUBLIC_KEY_SIZE * 2 + 1];
  47. };
  48.  
  49. struct DHT_node bootstrap_nodes[] =
  50. {
  51.  
  52. // Setup tox bootrap nodes
  53.  
  54. { "node.tox.biribiri.org", 33445,
  55. "F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67" },
  56. { "128.199.199.197", 33445,
  57. "B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09" },
  58. { "2400:6180:0:d0::17a:a001", 33445,
  59. "B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09" }, };
  60.  
  61. #define LINE_MAX_SIZE 512 // If input line's length surpassed this value, it will be truncated.
  62.  
  63. #define PORT_RANGE_START 33445 // tox listen port range
  64. #define PORT_RANGE_END 34445
  65.  
  66. #define AREPL_INTERVAL 30 // Async REPL iterate interval. unit: millisecond.
  67.  
  68. #define DEFAULT_CHAT_HIST_COUNT 20 // how many items of chat history to show by default;
  69.  
  70. #define SAVEDATA_AFTER_COMMAND true // whether save data after executing any command
  71.  
  72. /// Macros for terminal display
  73.  
  74. #define CODE_ERASE_LINE "\r\033[2K"
  75.  
  76. #define RESET_COLOR "\x01b[0m"
  77. #define SELF_TALK_COLOR "\x01b[35m" // magenta
  78. #define GUEST_TALK_COLOR "\x01b[90m" // bright black
  79. #define CMD_PROMPT_COLOR "\x01b[34m" // blue
  80.  
  81. #define CMD_PROMPT CMD_PROMPT_COLOR "> " RESET_COLOR // green
  82. #define FRIEND_TALK_PROMPT CMD_PROMPT_COLOR "%-.12s << " RESET_COLOR
  83. #define GROUP_TALK_PROMPT CMD_PROMPT_COLOR "%-.12s <<< " RESET_COLOR
  84.  
  85. #define GUEST_MSG_PREFIX GUEST_TALK_COLOR "%s %12.12s | " RESET_COLOR
  86. #define SELF_MSG_PREFIX SELF_TALK_COLOR "%s %12.12s | " RESET_COLOR
  87. #define CMD_MSG_PREFIX CMD_PROMPT
  88.  
  89. #define PRINT(_fmt, ...) \
  90. fputs(CODE_ERASE_LINE,stdout);\
  91. printf(_fmt "\n", ##__VA_ARGS__);
  92.  
  93. #define COLOR_PRINT(_color, _fmt,...) PRINT(_color _fmt RESET_COLOR, ##__VA_ARGS__)
  94.  
  95. #define INFO(_fmt,...) COLOR_PRINT("\x01b[36m", _fmt, ##__VA_ARGS__) // cyran
  96. #define WARN(_fmt,...) COLOR_PRINT("\x01b[33m", _fmt, ##__VA_ARGS__) // yellow
  97. #define ERROR(_fmt,...) COLOR_PRINT("\x01b[31m", _fmt, ##__VA_ARGS__) // red
  98.  
  99. /*******************************************************************************
  100. *
  101. * Headers
  102. *
  103. ******************************************************************************/
  104.  
  105. // TODO ADDED BY ME
  106.  
  107. struct Messenger {
  108. void *log;
  109. void *mono_time;
  110. void *net;
  111. void *net_crypto;
  112. void *dht;
  113.  
  114. };
  115.  
  116. struct Tox {
  117. struct Messenger *m;
  118. };
  119.  
  120. // TODO END ADDED BY ME
  121.  
  122. struct Tox *tox;
  123.  
  124. typedef void CommandHandler(int narg, char **args);
  125.  
  126. struct Command {
  127. char *name;
  128. char *desc;
  129. int narg;
  130. CommandHandler *handler;
  131. };
  132.  
  133. struct GroupUserData {
  134. uint32_t friend_num;
  135. uint8_t *cookie;
  136. size_t length;
  137. };
  138.  
  139. struct FriendUserData {
  140. uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
  141. };
  142.  
  143. union RequestUserData {
  144. struct GroupUserData group;
  145. struct FriendUserData friend;
  146. };
  147.  
  148. struct Request {
  149. char *msg;
  150. uint32_t id;
  151. bool is_friend_request;
  152. union RequestUserData userdata;
  153. struct Request *next;
  154. };
  155.  
  156. struct ChatHist {
  157. char *msg;
  158. struct ChatHist *next;
  159. struct ChatHist *prev;
  160. };
  161.  
  162. struct GroupPeer {
  163. uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
  164. char name[TOX_MAX_NAME_LENGTH + 1];
  165. };
  166.  
  167. struct Group {
  168. uint32_t group_num;
  169. char *title;
  170. struct GroupPeer *peers;
  171. size_t peers_count;
  172.  
  173. struct ChatHist *hist;
  174.  
  175. struct Group *next;
  176. };
  177.  
  178. struct Friend {
  179. uint32_t friend_num;
  180. char *name;
  181. char *status_message;
  182. uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
  183. TOX_CONNECTION connection;
  184.  
  185. struct ChatHist *hist;
  186.  
  187. struct Friend *next;
  188. };
  189.  
  190. int NEW_STDIN_FILENO = STDIN_FILENO;
  191.  
  192. struct Request *requests = NULL;
  193.  
  194. struct Friend *friends = NULL;
  195. struct Friend self;
  196. struct Group *groups = NULL;
  197.  
  198. enum TALK_TYPE {
  199. TALK_TYPE_FRIEND,
  200. TALK_TYPE_GROUP,
  201. TALK_TYPE_COUNT,
  202. TALK_TYPE_NULL = UINT32_MAX
  203. };
  204.  
  205. uint32_t TalkingTo = TALK_TYPE_NULL;
  206.  
  207. /*******************************************************************************
  208. *
  209. * Utils
  210. *
  211. ******************************************************************************/
  212.  
  213. #define RESIZE(key, size_key, length) \
  214. if ((size_key) < (length + 1)) { \
  215. size_key = (length+1);\
  216. key = calloc(1, size_key);\
  217. }
  218.  
  219. #define LIST_FIND(_p, _condition) \
  220. for (;*(_p) != NULL;_p = &((*_p)->next)) { \
  221. if (_condition) { \
  222. break;\
  223. }\
  224. }\
  225.  
  226. #define INDEX_TO_TYPE(idx) (idx % TALK_TYPE_COUNT)
  227. #define INDEX_TO_NUM(idx) (idx / TALK_TYPE_COUNT)
  228. #define GEN_INDEX(num,type) (num * TALK_TYPE_COUNT + type)
  229.  
  230. bool str2uint(char *str, uint32_t *num) {
  231. char *str_end;
  232. long l = strtol(str, &str_end, 10);
  233. if (str_end == str || l < 0)
  234. return false;
  235. *num = (uint32_t) l;
  236. return true;
  237. }
  238.  
  239. char* genmsg(struct ChatHist **pp, const char *fmt, ...) {
  240. va_list va;
  241. va_start(va, fmt);
  242.  
  243. va_list va2;
  244. va_copy(va2, va);
  245. size_t len = vsnprintf(NULL, 0, fmt, va2);
  246. va_end(va2);
  247.  
  248. struct ChatHist *h = malloc(sizeof(struct ChatHist));
  249. h->prev = NULL;
  250. h->next = (*pp);
  251. if (*pp)
  252. (*pp)->prev = h;
  253. *pp = h;
  254. h->msg = malloc(len + 1);
  255.  
  256. vsnprintf(h->msg, len + 1, fmt, va);
  257. va_end(va);
  258.  
  259. return h->msg;
  260. }
  261.  
  262. char* getftime(void) {
  263. static char timebuf[64];
  264.  
  265. time_t tt = time(NULL);
  266. struct tm *tm = localtime(&tt);
  267. strftime(timebuf, sizeof(timebuf), "%H:%M:%S", tm);
  268. return timebuf;
  269. }
  270.  
  271. const char* connection_enum2text(TOX_CONNECTION conn) {
  272. switch (conn) {
  273. case TOX_CONNECTION_NONE:
  274. return "Offline";
  275. case TOX_CONNECTION_TCP:
  276. return "Online(TCP)";
  277. case TOX_CONNECTION_UDP:
  278. return "Online(UDP)";
  279. default:
  280. return "UNKNOWN";
  281. }
  282. }
  283.  
  284. struct Friend* getfriend(uint32_t friend_num) {
  285. struct Friend **p = &friends;
  286. LIST_FIND(p, (*p)->friend_num == friend_num);
  287. return *p;
  288. }
  289.  
  290. struct Friend* addfriend(uint32_t friend_num) {
  291. struct Friend *f = calloc(1, sizeof(struct Friend));
  292. f->next = friends;
  293. friends = f;
  294. f->friend_num = friend_num;
  295. f->connection = TOX_CONNECTION_NONE;
  296. tox_friend_get_public_key(tox, friend_num, f->pubkey, NULL);
  297. return f;
  298. }
  299.  
  300. bool delfriend(uint32_t friend_num) {
  301. struct Friend **p = &friends;
  302. LIST_FIND(p, (*p)->friend_num == friend_num);
  303. struct Friend *f = *p;
  304. if (f) {
  305. *p = f->next;
  306. if (f->name)
  307. free(f->name);
  308. if (f->status_message)
  309. free(f->status_message);
  310. while (f->hist) {
  311. struct ChatHist *tmp = f->hist;
  312. f->hist = f->hist->next;
  313. free(tmp);
  314. }
  315. free(f);
  316. return 1;
  317. }
  318. return 0;
  319. }
  320.  
  321. struct Group* addgroup(uint32_t group_num) {
  322. struct Group *cf = calloc(1, sizeof(struct Group));
  323. cf->next = groups;
  324. groups = cf;
  325.  
  326. cf->group_num = group_num;
  327.  
  328. return cf;
  329. }
  330.  
  331. bool delgroup(uint32_t group_num) {
  332. struct Group **p = &groups;
  333. LIST_FIND(p, (*p)->group_num == group_num);
  334. struct Group *cf = *p;
  335. if (cf) {
  336. *p = cf->next;
  337. if (cf->peers)
  338. free(cf->peers);
  339. if (cf->title)
  340. free(cf->title);
  341. while (cf->hist) {
  342. struct ChatHist *tmp = cf->hist;
  343. cf->hist = cf->hist->next;
  344. free(tmp);
  345. }
  346. free(cf);
  347. return 1;
  348. }
  349. return 0;
  350. }
  351.  
  352. struct Group* getgroup(uint32_t group_num) {
  353. struct Group **p = &groups;
  354. LIST_FIND(p, (*p)->group_num == group_num);
  355. return *p;
  356. }
  357.  
  358. uint8_t* hex2bin(const char *hex) {
  359. size_t len = strlen(hex) / 2;
  360. uint8_t *bin = malloc(len);
  361.  
  362. for (size_t i = 0; i < len; ++i, hex += 2) {
  363. sscanf(hex, "%2hhx", &bin[i]);
  364. }
  365.  
  366. return bin;
  367. }
  368.  
  369. char* bin2hex(const uint8_t *bin, size_t length) {
  370. char *hex = malloc(2 * length + 1);
  371. char *saved = hex;
  372. for (int i = 0; i < length; i++, hex += 2) {
  373. sprintf(hex, "%02X", bin[i]);
  374. }
  375. return saved;
  376. }
  377.  
  378. struct ChatHist** get_current_histp(void) {
  379. if (TalkingTo == TALK_TYPE_NULL)
  380. return NULL;
  381. uint32_t num = INDEX_TO_NUM(TalkingTo);
  382. switch (INDEX_TO_TYPE(TalkingTo)) {
  383. case TALK_TYPE_FRIEND: {
  384. struct Friend *f = getfriend(num);
  385. if (f)
  386. return &f->hist;
  387. break;
  388. }
  389. case TALK_TYPE_GROUP: {
  390. struct Group *cf = getgroup(num);
  391. if (cf)
  392. return &cf->hist;
  393. break;
  394. }
  395. }
  396. return NULL;
  397. }
  398.  
  399. /*******************************************************************************
  400. *
  401. * Async REPL
  402. *
  403. ******************************************************************************/
  404.  
  405. struct AsyncREPL {
  406. char *line;
  407. char *prompt;
  408. size_t sz;
  409. int nbuf;
  410. int nstack;
  411. };
  412.  
  413. struct termios saved_tattr;
  414.  
  415. struct AsyncREPL *async_repl;
  416.  
  417. void arepl_exit(void) {
  418. tcsetattr(NEW_STDIN_FILENO, TCSAFLUSH, &saved_tattr);
  419. }
  420.  
  421. void setup_arepl(void) {
  422. if (!(isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))) {
  423. fputs("! stdout & stdin should be connected to tty", stderr);
  424. exit(1);
  425. }
  426. async_repl = malloc(sizeof(struct AsyncREPL));
  427. async_repl->nbuf = 0;
  428. async_repl->nstack = 0;
  429. async_repl->sz = LINE_MAX_SIZE;
  430. async_repl->line = malloc(LINE_MAX_SIZE);
  431. async_repl->prompt = malloc(LINE_MAX_SIZE);
  432.  
  433. strcpy(async_repl->prompt, CMD_PROMPT);
  434.  
  435. // stdin and stdout may share the same file obj,
  436. // reopen stdin to avoid accidentally getting stdout modified.
  437.  
  438. char stdin_path[4080]; // 4080 is large enough for a path length for *nix system.
  439. #ifdef F_GETPATH // macosx
  440. if (fcntl(STDIN_FILENO, F_GETPATH, stdin_path) == -1) {
  441. fputs("! fcntl get stdin filepath failed", stderr);
  442. exit(1);
  443. }
  444. #else // linux
  445. if (readlink("/proc/self/fd/0", stdin_path, sizeof(stdin_path)) == -1) {
  446. fputs("! get stdin filename failed", stderr);
  447. exit(1);
  448. }
  449. #endif
  450.  
  451. NEW_STDIN_FILENO = open(stdin_path, O_RDONLY);
  452. if (NEW_STDIN_FILENO == -1) {
  453. fputs("! reopen stdin failed", stderr);
  454. exit(1);
  455. }
  456. close(STDIN_FILENO);
  457.  
  458. // Set stdin to Non-Blocking
  459. int flags = fcntl(NEW_STDIN_FILENO, F_GETFL, 0);
  460. fcntl(NEW_STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
  461.  
  462. /* Set stdin to Non-Canonical terminal mode. */
  463. struct termios tattr;
  464. tcgetattr(NEW_STDIN_FILENO, &tattr);
  465. saved_tattr = tattr; // save it to restore when exit
  466. tattr.c_lflag &= ~(ICANON | ECHO); /* Clear ICANON. */
  467. tattr.c_cc[VMIN] = 1;
  468. tattr.c_cc[VTIME] = 0;
  469. tcsetattr(NEW_STDIN_FILENO, TCSAFLUSH, &tattr);
  470.  
  471. atexit(arepl_exit);
  472. }
  473.  
  474. void arepl_reprint(struct AsyncREPL *arepl) {
  475. fputs(CODE_ERASE_LINE, stdout);
  476. if (arepl->prompt)
  477. fputs(arepl->prompt, stdout);
  478. if (arepl->nbuf > 0)
  479. printf("%.*s", arepl->nbuf, arepl->line);
  480. if (arepl->nstack > 0) {
  481. printf("%.*s", (int) arepl->nstack,
  482. arepl->line + arepl->sz - arepl->nstack);
  483. printf("\033[%dD", arepl->nstack); // move cursor
  484. }
  485. fflush(stdout);
  486. }
  487.  
  488. #define _AREPL_CURSOR_LEFT() arepl->line[arepl->sz - (++arepl->nstack)] = arepl->line[--arepl->nbuf]
  489. #define _AREPL_CURSOR_RIGHT() arepl->line[arepl->nbuf++] = arepl->line[arepl->sz - (arepl->nstack--)]
  490.  
  491. int arepl_readline(struct AsyncREPL *arepl, char c, char *line, size_t sz) {
  492. static uint32_t escaped = 0;
  493. if (c == '\033') { // mark escape code
  494. escaped = 1;
  495. return 0;
  496. }
  497.  
  498. if (escaped > 0)
  499. escaped++;
  500.  
  501. switch (c) {
  502. case '\n': {
  503. int ret = snprintf(line, sz, "%.*s%.*s\n", (int )arepl->nbuf,
  504. arepl->line, (int )arepl->nstack,
  505. arepl->line + arepl->sz - arepl->nstack);
  506. arepl->nbuf = 0;
  507. arepl->nstack = 0;
  508. return ret;
  509. }
  510.  
  511. case '\010': // C-h
  512. case '\177': // Backspace
  513. if (arepl->nbuf > 0)
  514. arepl->nbuf--;
  515. break;
  516. case '\025': // C-u
  517. arepl->nbuf = 0;
  518. break;
  519. case '\013': // C-k Vertical Tab
  520. arepl->nstack = 0;
  521. break;
  522. case '\001': // C-a
  523. while (arepl->nbuf > 0)
  524. _AREPL_CURSOR_LEFT();
  525. break;
  526. case '\005': // C-e
  527. while (arepl->nstack > 0)
  528. _AREPL_CURSOR_RIGHT();
  529. break;
  530. case '\002': // C-b
  531. if (arepl->nbuf > 0)
  532. _AREPL_CURSOR_LEFT();
  533. break;
  534. case '\006': // C-f
  535. if (arepl->nstack > 0)
  536. _AREPL_CURSOR_RIGHT();
  537. break;
  538. case '\027': // C-w: backward delete a word
  539. while (arepl->nbuf > 0 && arepl->line[arepl->nbuf - 1] == ' ')
  540. arepl->nbuf--;
  541. while (arepl->nbuf > 0 && arepl->line[arepl->nbuf - 1] != ' ')
  542. arepl->nbuf--;
  543. break;
  544.  
  545. case 'D':
  546. case 'C':
  547. if (escaped == 3 && arepl->nbuf >= 1
  548. && arepl->line[arepl->nbuf - 1] == '[') { // arrow keys
  549. arepl->nbuf--;
  550. if (c == 'D' && arepl->nbuf > 0)
  551. _AREPL_CURSOR_LEFT(); // left arrow: \033[D
  552. if (c == 'C' && arepl->nstack > 0)
  553. _AREPL_CURSOR_RIGHT(); // right arrow: \033[C
  554. break;
  555. }
  556. // fall through to default case
  557. default:
  558. arepl->line[arepl->nbuf++] = c;
  559. }
  560. return 0;
  561. }
  562.  
  563. /*******************************************************************************
  564. *
  565. * Tox Callbacks
  566. *
  567. ******************************************************************************/
  568.  
  569. void friend_message_cb(Tox *tox, uint32_t friend_num, TOX_MESSAGE_TYPE type,
  570. const uint8_t *message, size_t length, void *user_data) {
  571. struct Friend *f = getfriend(friend_num);
  572. if (!f)
  573. return;
  574. if (type != TOX_MESSAGE_TYPE_NORMAL) {
  575. INFO("* receive MESSAGE ACTION type from %s, no supported", f->name);
  576. return;
  577. }
  578.  
  579. char *msg = genmsg(&f->hist, GUEST_MSG_PREFIX "%.*s", getftime(), f->name,
  580. (int) length, (char*) message);
  581. if (GEN_INDEX(friend_num, TALK_TYPE_FRIEND) == TalkingTo) {
  582. PRINT("%s", msg);
  583. } else {
  584. INFO("* receive message from %s, use `/go <contact_index>` to talk\n",
  585. f->name);
  586. }
  587. }
  588.  
  589. void friend_name_cb(Tox *tox, uint32_t friend_num, const uint8_t *name,
  590. size_t length, void *user_data) {
  591. struct Friend *f = getfriend(friend_num);
  592.  
  593. if (f) {
  594. f->name = realloc(f->name, length + 1);
  595. sprintf(f->name, "%.*s", (int )length, (char* )name);
  596. if (GEN_INDEX(friend_num, TALK_TYPE_FRIEND) == TalkingTo) {
  597. INFO("* Opposite changed name to %.*s", (int )length, (char* )name)
  598. sprintf(async_repl->prompt, FRIEND_TALK_PROMPT, f->name);
  599. }
  600. }
  601. }
  602.  
  603. void friend_status_message_cb(Tox *tox, uint32_t friend_num,
  604. const uint8_t *message, size_t length, void *user_data) {
  605. struct Friend *f = getfriend(friend_num);
  606. if (f) {
  607. f->status_message = realloc(f->status_message, length + 1);
  608. sprintf(f->status_message, "%.*s", (int )length, (char* )message);
  609. }
  610. }
  611.  
  612. void friend_connection_status_cb(Tox *tox, uint32_t friend_num,
  613. TOX_CONNECTION connection_status, void *user_data) {
  614. struct Friend *f = getfriend(friend_num);
  615. if (f) {
  616. f->connection = connection_status;
  617. INFO("* %s is %s", f->name, connection_enum2text(connection_status));
  618. }
  619. }
  620.  
  621. void friend_request_cb(Tox *tox, const uint8_t *public_key,
  622. const uint8_t *message, size_t length, void *user_data) {
  623. INFO("* receive friend request(use `/accept` to see).");
  624.  
  625. struct Request *req = malloc(sizeof(struct Request));
  626.  
  627. req->id = 1 + ((requests != NULL) ? requests->id : 0);
  628. req->is_friend_request = true;
  629. memcpy(req->userdata.friend.pubkey, public_key, TOX_PUBLIC_KEY_SIZE);
  630. req->msg = malloc(length + 1);
  631. sprintf(req->msg, "%.*s", (int )length, (char* )message);
  632.  
  633. req->next = requests;
  634. requests = req;
  635. }
  636.  
  637. void self_connection_status_cb(Tox *tox, TOX_CONNECTION connection_status,
  638. void *user_data) {
  639. self.connection = connection_status;
  640. INFO("* You are %s", connection_enum2text(connection_status));
  641. }
  642.  
  643. void group_invite_cb(Tox *tox, uint32_t friend_num, TOX_CONFERENCE_TYPE type,
  644. const uint8_t *cookie, size_t length, void *user_data) {
  645. struct Friend *f = getfriend(friend_num);
  646. if (f) {
  647. if (type == TOX_CONFERENCE_TYPE_AV) {
  648. WARN(
  649. "* %s invites you to an AV group, which has not been supported.",
  650. f->name);
  651. return;
  652. }
  653. INFO("* %s invites you to a group(try `/accept` to see)", f->name);
  654. struct Request *req = malloc(sizeof(struct Request));
  655. req->id = 1 + ((requests != NULL) ? requests->id : 0);
  656. req->next = requests;
  657. requests = req;
  658.  
  659. req->is_friend_request = false;
  660. req->userdata.group.cookie = malloc(length);
  661. memcpy(req->userdata.group.cookie, cookie, length), req->userdata.group.length =
  662. length;
  663. req->userdata.group.friend_num = friend_num;
  664. int sz = snprintf(NULL, 0, "%s%s", "From ", f->name);
  665. req->msg = malloc(sz + 1);
  666. sprintf(req->msg, "%s%s", "From ", f->name);
  667. }
  668. }
  669.  
  670. void group_title_cb(Tox *tox, uint32_t group_num, uint32_t peer_number,
  671. const uint8_t *title, size_t length, void *user_data) {
  672. struct Group *cf = getgroup(group_num);
  673. if (cf) {
  674. cf->title = realloc(cf->title, length + 1);
  675. sprintf(cf->title, "%.*s", (int )length, (char* )title);
  676. if (GEN_INDEX(group_num, TALK_TYPE_GROUP) == TalkingTo) {
  677. INFO("* Group title changed to %s", cf->title);
  678. sprintf(async_repl->prompt, GROUP_TALK_PROMPT, cf->title);
  679. }
  680. }
  681. }
  682.  
  683. void group_message_cb(Tox *tox, uint32_t group_num, uint32_t peer_number,
  684. TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length,
  685. void *user_data) {
  686. struct Group *cf = getgroup(group_num);
  687. if (!cf)
  688. return;
  689.  
  690. if (tox_conference_peer_number_is_ours(tox, group_num, peer_number, NULL))
  691. return;
  692.  
  693. if (type != TOX_MESSAGE_TYPE_NORMAL) {
  694. INFO("* receive MESSAGE ACTION type from group %s, no supported",
  695. cf->title);
  696. return;
  697. }
  698. if (peer_number >= cf->peers_count) {
  699. ERROR("! Unknown peer_number, peer_count:%zu, peer_number:%u",
  700. cf->peers_count, peer_number);
  701. return;
  702. }
  703.  
  704. struct GroupPeer *peer = &cf->peers[peer_number];
  705. char *msg = genmsg(&cf->hist, GUEST_MSG_PREFIX "%.*s", getftime(),
  706. peer->name, (int) length, (char*) message);
  707.  
  708. if (GEN_INDEX(group_num, TALK_TYPE_GROUP) == TalkingTo) {
  709. PRINT("%s", msg);
  710. } else {
  711. INFO("* receive group message from %s, in group %s", peer->name,
  712. cf->title);
  713. }
  714. }
  715.  
  716. void group_peer_list_changed_cb(Tox *tox, uint32_t group_num, void *user_data) {
  717. struct Group *cf = getgroup(group_num);
  718. if (!cf)
  719. return;
  720.  
  721. TOX_ERR_CONFERENCE_PEER_QUERY err;
  722. uint32_t count = tox_conference_peer_count(tox, group_num, &err);
  723. if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
  724. ERROR("get group peer count failed, errcode:%d", err);
  725. return;
  726. }
  727. if (cf->peers)
  728. free(cf->peers);
  729. cf->peers = calloc(count, sizeof(struct GroupPeer));
  730. cf->peers_count = count;
  731.  
  732. for (int i = 0; i < count; i++) {
  733. struct GroupPeer *p = cf->peers + i;
  734. tox_conference_peer_get_name(tox, group_num, i, (uint8_t*) p->name,
  735. NULL);
  736. tox_conference_peer_get_public_key(tox, group_num, i, p->pubkey, NULL);
  737. }
  738. }
  739. void group_peer_name_cb(Tox *tox, uint32_t group_num, uint32_t peer_num,
  740. const uint8_t *name, size_t length, void *user_data) {
  741. struct Group *cf = getgroup(group_num);
  742. if (!cf || peer_num >= cf->peers_count) {
  743. ERROR("! Unexpected group_num/peer_num in group_peer_name_cb");
  744. return;
  745. }
  746.  
  747. struct GroupPeer *p = &cf->peers[peer_num];
  748. sprintf(p->name, "%.*s", (int )length, (char* )name);
  749. }
  750.  
  751. /*******************************************************************************
  752. *
  753. * Tox Setup
  754. *
  755. ******************************************************************************/
  756.  
  757. void create_tox(void) {
  758. struct Tox_Options *options = tox_options_new(NULL);
  759. tox_options_set_start_port(options, PORT_RANGE_START);
  760. tox_options_set_end_port(options, PORT_RANGE_END);
  761.  
  762. if (savedata_filename) {
  763. FILE *f = fopen(savedata_filename, "rb");
  764. if (f) {
  765. fseek(f, 0, SEEK_END);
  766. long fsize = ftell(f);
  767. fseek(f, 0, SEEK_SET);
  768.  
  769. char *savedata = malloc(fsize);
  770. fread(savedata, fsize, 1, f);
  771. fclose(f);
  772.  
  773. tox_options_set_savedata_type(options, TOX_SAVEDATA_TYPE_TOX_SAVE);
  774. tox_options_set_savedata_data(options, (uint8_t*) savedata, fsize);
  775.  
  776. tox = tox_new(options, NULL);
  777.  
  778. free(savedata);
  779. }
  780. }
  781.  
  782. if (!tox)
  783. tox = tox_new(options, NULL);
  784. tox_options_free(options);
  785. }
  786.  
  787. void init_friends(void) {
  788. size_t sz = tox_self_get_friend_list_size(tox);
  789. uint32_t *friend_list = malloc(sizeof(uint32_t) * sz);
  790. tox_self_get_friend_list(tox, friend_list);
  791.  
  792. size_t len;
  793.  
  794. for (int i = 0; i < sz; i++) {
  795. uint32_t friend_num = friend_list[i];
  796. struct Friend *f = addfriend(friend_num);
  797.  
  798. len = tox_friend_get_name_size(tox, friend_num, NULL) + 1;
  799. f->name = calloc(1, len);
  800. tox_friend_get_name(tox, friend_num, (uint8_t*) f->name, NULL);
  801.  
  802. len = tox_friend_get_status_message_size(tox, friend_num, NULL) + 1;
  803. f->status_message = calloc(1, len);
  804. tox_friend_get_status_message(tox, friend_num,
  805. (uint8_t*) f->status_message, NULL);
  806.  
  807. tox_friend_get_public_key(tox, friend_num, f->pubkey, NULL);
  808. }
  809. free(friend_list);
  810.  
  811. // add self
  812. self.friend_num = TALK_TYPE_NULL;
  813. len = tox_self_get_name_size(tox) + 1;
  814. self.name = calloc(1, len);
  815. tox_self_get_name(tox, (uint8_t*) self.name);
  816.  
  817. len = tox_self_get_status_message_size(tox) + 1;
  818. self.status_message = calloc(1, len);
  819. tox_self_get_status_message(tox, (uint8_t*) self.status_message);
  820.  
  821. tox_self_get_public_key(tox, self.pubkey);
  822. }
  823.  
  824. void update_savedata_file(void) {
  825. if (!(savedata_filename && savedata_tmp_filename))
  826. return;
  827.  
  828. size_t size = tox_get_savedata_size(tox);
  829. char *savedata = malloc(size);
  830. tox_get_savedata(tox, (uint8_t*) savedata);
  831.  
  832. FILE *f = fopen(savedata_tmp_filename, "wb");
  833. fwrite(savedata, size, 1, f);
  834. fclose(f);
  835.  
  836. rename(savedata_tmp_filename, savedata_filename);
  837.  
  838. free(savedata);
  839. }
  840.  
  841. void bootstrap(void) {
  842. for (size_t i = 0; i < sizeof(bootstrap_nodes) / sizeof(struct DHT_node);
  843. i++) {
  844. uint8_t *bin = hex2bin(bootstrap_nodes[i].key_hex);
  845. tox_bootstrap(tox, bootstrap_nodes[i].ip, bootstrap_nodes[i].port, bin,
  846. NULL);
  847. free(bin);
  848. }
  849. }
  850.  
  851. void setup_tox(void) {
  852. create_tox();
  853. init_friends();
  854. bootstrap();
  855.  
  856. ////// register callbacks
  857.  
  858. // self
  859. tox_callback_self_connection_status(tox, self_connection_status_cb);
  860.  
  861. // friend
  862. tox_callback_friend_request(tox, friend_request_cb);
  863. tox_callback_friend_message(tox, friend_message_cb);
  864. tox_callback_friend_name(tox, friend_name_cb);
  865. tox_callback_friend_status_message(tox, friend_status_message_cb);
  866. tox_callback_friend_connection_status(tox, friend_connection_status_cb);
  867.  
  868. // group
  869. tox_callback_conference_invite(tox, group_invite_cb);
  870. tox_callback_conference_title(tox, group_title_cb);
  871. tox_callback_conference_message(tox, group_message_cb);
  872. tox_callback_conference_peer_list_changed(tox, group_peer_list_changed_cb);
  873. tox_callback_conference_peer_name(tox, group_peer_name_cb);
  874. }
  875.  
  876. /*******************************************************************************
  877. *
  878. * Commands
  879. *
  880. ******************************************************************************/
  881.  
  882. void command_help(int narg, char **args);
  883.  
  884. void command_guide(int narg, char **args) {
  885. PRINT("This program is an minimal workable implementation of Tox client.");
  886. PRINT("As it pursued simplicity at the cost of robustness and efficiency,");
  887. PRINT(
  888. "It should only be used for learning or playing with, instead of daily use.\n");
  889.  
  890. PRINT("Commands are any input lines with leading `/`,");
  891. PRINT("Command args are seprated by blanks,");
  892. PRINT(
  893. "while some special commands may accept any-character string, like `/setname` and `/setstmsg`.\n");
  894.  
  895. PRINT("Use `/setname <YOUR NAME>` to set your name");
  896. PRINT("Use `/info` to see your Name, Tox Id and Network Connection.");
  897. PRINT(
  898. "Use `/contacts` to list friends and groups, and use `/go <TARGET>` to talk to one of them.");
  899. PRINT("Finally, use `/help` to get a list of available commands.\n");
  900.  
  901. PRINT("HAVE FUN!\n")
  902. }
  903.  
  904. void _print_friend_info(struct Friend *f, bool is_self) {
  905. PRINT("%-15s%s", "Name:", f->name);
  906.  
  907. if (is_self) {
  908. uint8_t tox_id_bin[TOX_ADDRESS_SIZE];
  909. tox_self_get_address(tox, tox_id_bin);
  910. char *hex = bin2hex(tox_id_bin, sizeof(tox_id_bin));
  911. PRINT("%-15s%s", "Tox ID:", hex);
  912. free(hex);
  913. }
  914.  
  915. char *hex = bin2hex(f->pubkey, sizeof(f->pubkey));
  916. PRINT("%-15s%s", "Public Key:", hex);
  917. free(hex);
  918. PRINT("%-15s%s", "Status Msg:", f->status_message);
  919. PRINT("%-15s%s", "Network:", connection_enum2text(f->connection));
  920. }
  921.  
  922. void command_info(int narg, char **args) {
  923. if (narg == 0) { // self
  924. _print_friend_info(&self, true);
  925. return;
  926. }
  927.  
  928. uint32_t contact_idx;
  929. if (!str2uint(args[0], &contact_idx))
  930. goto FAIL;
  931.  
  932. uint32_t num = INDEX_TO_NUM(contact_idx);
  933. switch (INDEX_TO_TYPE(contact_idx)) {
  934. case TALK_TYPE_FRIEND: {
  935. struct Friend *f = getfriend(num);
  936. if (f) {
  937. _print_friend_info(f, false);
  938. return;
  939. }
  940. break;
  941. }
  942. case TALK_TYPE_GROUP: {
  943. struct Group *cf = getgroup(num);
  944. if (cf) {
  945. PRINT("GROUP TITLE:\t%s", cf->title);
  946. PRINT("PEER COUNT:\t%zu", cf->peers_count);
  947. PRINT("Peers:");
  948. for (int i = 0; i < cf->peers_count; i++) {
  949. PRINT("\t%s", cf->peers[i].name);
  950. }
  951. return;
  952. }
  953. break;
  954. }
  955. }
  956. FAIL:
  957. WARN("^ Invalid contact index");
  958. }
  959.  
  960. void command_setname(int narg, char **args) {
  961. char *name = args[0];
  962. size_t len = strlen(name);
  963. TOX_ERR_SET_INFO err;
  964. tox_self_set_name(tox, (uint8_t*) name, strlen(name), &err);
  965.  
  966. if (err != TOX_ERR_SET_INFO_OK) {
  967. ERROR("! set name failed, errcode:%d", err);
  968. return;
  969. }
  970.  
  971. self.name = realloc(self.name, len + 1);
  972. strcpy(self.name, name);
  973. }
  974.  
  975. void command_setstmsg(int narg, char **args) {
  976. char *status = args[0];
  977. size_t len = strlen(status);
  978. TOX_ERR_SET_INFO err;
  979. tox_self_set_status_message(tox, (uint8_t*) status, strlen(status), &err);
  980. if (err != TOX_ERR_SET_INFO_OK) {
  981. ERROR("! set status message failed, errcode:%d", err);
  982. return;
  983. }
  984.  
  985. self.status_message = realloc(self.status_message, len + 1);
  986. strcpy(self.status_message, status);
  987. }
  988.  
  989. void command_add(int narg, char **args) {
  990. char *hex_id = args[0];
  991. char *msg = "";
  992. if (narg > 1)
  993. msg = args[1];
  994.  
  995. uint8_t *bin_id = hex2bin(hex_id);
  996. TOX_ERR_FRIEND_ADD err;
  997. uint32_t friend_num = tox_friend_add(tox, bin_id, (uint8_t*) msg,
  998. strlen(msg), &err);
  999. free(bin_id);
  1000.  
  1001. if (err != TOX_ERR_FRIEND_ADD_OK) {
  1002. ERROR("! add friend failed, errcode:%d", err);
  1003. return;
  1004. }
  1005.  
  1006. addfriend(friend_num);
  1007. }
  1008.  
  1009. void command_del(int narg, char **args) {
  1010. uint32_t contact_idx;
  1011. if (!str2uint(args[0], &contact_idx))
  1012. goto FAIL;
  1013. uint32_t num = INDEX_TO_NUM(contact_idx);
  1014. switch (INDEX_TO_TYPE(contact_idx)) {
  1015. case TALK_TYPE_FRIEND:
  1016. if (delfriend(num)) {
  1017. tox_friend_delete(tox, num, NULL);
  1018. return;
  1019. }
  1020. break;
  1021. case TALK_TYPE_GROUP:
  1022. if (delgroup(num)) {
  1023. tox_conference_delete(tox, num, NULL);
  1024. return;
  1025. }
  1026. break;
  1027. }
  1028. FAIL:
  1029. WARN("^ Invalid contact index");
  1030. }
  1031.  
  1032. void command_contacts(int narg, char **args) {
  1033. struct Friend *f = friends;
  1034. PRINT("#Friends(conctact_index|name|connection|status message):\n");
  1035. for (; f != NULL; f = f->next) {
  1036. PRINT("%3d %15.15s %12.12s %s",
  1037. GEN_INDEX(f->friend_num, TALK_TYPE_FRIEND), f->name,
  1038. connection_enum2text(f->connection), f->status_message);
  1039. }
  1040.  
  1041. struct Group *cf = groups;
  1042. PRINT("\n#Groups(contact_index|count of peers|name):\n");
  1043. for (; cf != NULL; cf = cf->next) {
  1044. PRINT("%3d %10d %s", GEN_INDEX(cf->group_num, TALK_TYPE_GROUP),
  1045. tox_conference_peer_count(tox, cf->group_num, NULL), cf->title);
  1046. }
  1047. }
  1048.  
  1049. void command_save(int narg, char **args) {
  1050. update_savedata_file();
  1051. }
  1052.  
  1053. void command_go(int narg, char **args) {
  1054. if (narg == 0) {
  1055. TalkingTo = TALK_TYPE_NULL;
  1056. strcpy(async_repl->prompt, CMD_PROMPT);
  1057. return;
  1058. }
  1059. uint32_t contact_idx;
  1060. if (!str2uint(args[0], &contact_idx))
  1061. goto FAIL;
  1062. uint32_t num = INDEX_TO_NUM(contact_idx);
  1063. switch (INDEX_TO_TYPE(contact_idx)) {
  1064. case TALK_TYPE_FRIEND: {
  1065. struct Friend *f = getfriend(num);
  1066. if (f) {
  1067. TalkingTo = contact_idx;
  1068. sprintf(async_repl->prompt, FRIEND_TALK_PROMPT, f->name);
  1069. return;
  1070. }
  1071. break;
  1072. }
  1073. case TALK_TYPE_GROUP: {
  1074. struct Group *cf = getgroup(num);
  1075. if (cf) {
  1076. TalkingTo = contact_idx;
  1077. sprintf(async_repl->prompt, GROUP_TALK_PROMPT, cf->title);
  1078. return;
  1079. }
  1080. break;
  1081. }
  1082. }
  1083.  
  1084. FAIL:
  1085. WARN("^ Invalid contact index");
  1086. }
  1087.  
  1088. void command_history(int narg, char **args) {
  1089. uint32_t n = DEFAULT_CHAT_HIST_COUNT;
  1090. if (narg > 0 && !str2uint(args[0], &n)) {
  1091. WARN("Invalid args");
  1092. }
  1093.  
  1094. struct ChatHist **hp = get_current_histp();
  1095. if (!hp) {
  1096. WARN("you are not talking to someone");
  1097. return;
  1098. }
  1099.  
  1100. struct ChatHist *hist = *hp;
  1101.  
  1102. while (hist && hist->next)
  1103. hist = hist->next;
  1104. PRINT("%s", "------------ HISTORY BEGIN ---------------")
  1105. for (int i = 0; i < n && hist; i++, hist = hist->prev) {
  1106. printf("%s\n", hist->msg);
  1107. }
  1108. PRINT("%s", "------------ HISTORY END ---------------")
  1109. }
  1110.  
  1111. void _command_accept(int narg, char **args, bool is_accept) {
  1112. if (narg == 0) {
  1113. struct Request *req = requests;
  1114. for (; req != NULL; req = req->next) {
  1115. PRINT("%-9u%-12s%s", req->id,
  1116. (req->is_friend_request ? "FRIEND" : "GROUP"), req->msg);
  1117. }
  1118. return;
  1119. }
  1120.  
  1121. uint32_t request_idx;
  1122. if (!str2uint(args[0], &request_idx))
  1123. goto FAIL;
  1124. struct Request **p = &requests;
  1125. LIST_FIND(p, (*p)->id == request_idx);
  1126. struct Request *req = *p;
  1127. if (req) {
  1128. *p = req->next;
  1129. if (is_accept) {
  1130. if (req->is_friend_request) {
  1131. TOX_ERR_FRIEND_ADD err;
  1132. uint32_t friend_num = tox_friend_add_norequest(tox,
  1133. req->userdata.friend.pubkey, &err);
  1134. if (err != TOX_ERR_FRIEND_ADD_OK) {
  1135. ERROR("! accept friend request failed, errcode:%d", err);
  1136. } else {
  1137. addfriend(friend_num);
  1138. }
  1139. } else { // group invite
  1140. struct GroupUserData *data = &req->userdata.group;
  1141. TOX_ERR_CONFERENCE_JOIN err;
  1142. uint32_t group_num = tox_conference_join(tox, data->friend_num,
  1143. data->cookie, data->length, &err);
  1144. if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
  1145. ERROR("! join group failed, errcode: %d", err);
  1146. } else {
  1147. addgroup(group_num);
  1148. }
  1149. }
  1150. }
  1151. free(req->msg);
  1152. free(req);
  1153. return;
  1154. }
  1155. FAIL:
  1156. WARN("Invalid request index");
  1157. }
  1158.  
  1159. void command_accept(int narg, char **args) {
  1160. _command_accept(narg, args, true);
  1161. }
  1162.  
  1163. void command_deny(int narg, char **args) {
  1164. _command_accept(narg, args, false);
  1165. }
  1166.  
  1167. void command_invite(int narg, char **args) {
  1168. uint32_t friend_contact_idx;
  1169. if (!str2uint(args[0], &friend_contact_idx)
  1170. || INDEX_TO_TYPE(friend_contact_idx) != TALK_TYPE_FRIEND) {
  1171. WARN("Invalid friend contact index");
  1172. return;
  1173. }
  1174. int err;
  1175. uint32_t group_num;
  1176. if (narg == 1) {
  1177. group_num = tox_conference_new(tox, (TOX_ERR_CONFERENCE_NEW*) &err);
  1178. if (err != TOX_ERR_CONFERENCE_NEW_OK) {
  1179. ERROR("! Create group failed, errcode:%d", err);
  1180. return;
  1181. }
  1182. addgroup(group_num);
  1183. } else {
  1184. uint32_t group_contact_idx;
  1185. if (!str2uint(args[1], &group_contact_idx)
  1186. || INDEX_TO_TYPE(group_contact_idx) != TALK_TYPE_GROUP) {
  1187. ERROR("! Invalid group contact index");
  1188. return;
  1189. }
  1190. group_num = INDEX_TO_NUM(group_contact_idx);
  1191. }
  1192.  
  1193. uint32_t friend_num = INDEX_TO_NUM(friend_contact_idx);
  1194. tox_conference_invite(tox, friend_num, group_num,
  1195. (TOX_ERR_CONFERENCE_INVITE*) &err);
  1196. if (err != TOX_ERR_CONFERENCE_INVITE_OK) {
  1197. ERROR("! Group invite failed, errcode:%d", err);
  1198. return;
  1199. }
  1200. }
  1201.  
  1202. void command_settitle(int narg, char **args) {
  1203. uint32_t group_contact_idx;
  1204. if (!str2uint(args[0], &group_contact_idx)
  1205. || INDEX_TO_TYPE(group_contact_idx) != TALK_TYPE_GROUP) {
  1206. ERROR("! Invalid group contact index");
  1207. return;
  1208. }
  1209. uint32_t group_num = INDEX_TO_NUM(group_contact_idx);
  1210. struct Group *cf = getgroup(group_num);
  1211. if (!cf) {
  1212. ERROR("! Invalid group contact index");
  1213. return;
  1214. }
  1215.  
  1216. char *title = args[1];
  1217. size_t len = strlen(title);
  1218. TOX_ERR_CONFERENCE_TITLE err;
  1219. tox_conference_set_title(tox, group_num, (uint8_t*) title, len, &err);
  1220. if (err != TOX_ERR_CONFERENCE_TITLE_OK) {
  1221. ERROR("! Set group title failed, errcode: %d", err);
  1222. return;
  1223. }
  1224.  
  1225. cf->title = realloc(cf->title, len + 1);
  1226. sprintf(cf->title, "%.*s", (int )len, title);
  1227. }
  1228.  
  1229. #define COMMAND_ARGS_REST 10
  1230. #define COMMAND_LENGTH (sizeof(commands)/sizeof(struct Command))
  1231.  
  1232. struct Command commands[] =
  1233. { { "guide", "- print the guide", 0, command_guide, }, { "help",
  1234. "- print this message.", 0, command_help, }, { "save",
  1235. "- save your data.", 0, command_save, },
  1236. { "info",
  1237. "[<contact_index>] - show one contact's info, or yourself's info if <contact_index> is empty. ",
  1238. 0 + COMMAND_ARGS_REST, command_info, }, { "setname",
  1239. "<name> - set your name", 1, command_setname, }, {
  1240. "setstmsg",
  1241. "<status_message> - set your status message.", 1,
  1242. command_setstmsg, }, { "add",
  1243. "<toxid> <msg> - add friend", 2, command_add, }, {
  1244. "del", "<contact_index> - del a contact.", 1,
  1245. command_del, }, { "contacts",
  1246. "- list your contacts(friends and groups).", 0,
  1247. command_contacts, },
  1248. { "go",
  1249. "[<contact_index>] - goto talk to a contact, or goto cmd mode if <contact_index> is empty.",
  1250. 0 + COMMAND_ARGS_REST, command_go, },
  1251. { "history",
  1252. "[<n>] - show previous <n> items(default:10) of current chat history",
  1253. 0 + COMMAND_ARGS_REST, command_history, },
  1254. { "accept",
  1255. "[<request_index>] - accept or list(if no <request_index> was provided) friend/group requests.",
  1256. 0 + COMMAND_ARGS_REST, command_accept, },
  1257. { "deny",
  1258. "[<request_index>] - deny or list(if no <request_index> was provided) friend/group requests.",
  1259. 0 + COMMAND_ARGS_REST, command_deny, },
  1260. { "invite",
  1261. "<friend_contact_index> [<group_contact_index>] - invite a friend to a group chat. default: create a group.",
  1262. 1 + COMMAND_ARGS_REST, command_invite, }, { "settitle",
  1263. "<group_contact_index> <title> - set group title.", 2,
  1264. command_settitle, }, };
  1265.  
  1266. void command_help(int narg, char **args) {
  1267. for (int i = 1; i < COMMAND_LENGTH; i++) {
  1268. printf("%-16s%s\n", commands[i].name, commands[i].desc);
  1269. }
  1270. }
  1271.  
  1272. /*******************************************************************************
  1273. *
  1274. * Main
  1275. *
  1276. ******************************************************************************/
  1277.  
  1278. char* poptok(char **strp) {
  1279. static const char *dem = " \t";
  1280. char *save = *strp;
  1281. *strp = strpbrk(*strp, dem);
  1282. if (*strp == NULL)
  1283. return save;
  1284.  
  1285. *((*strp)++) = '\0';
  1286. *strp += strspn(*strp, dem);
  1287. return save;
  1288. }
  1289.  
  1290. void repl_iterate(void) {
  1291. static char buf[128];
  1292. static char line[LINE_MAX_SIZE];
  1293. while (1) {
  1294. int n = read(NEW_STDIN_FILENO, buf, sizeof(buf));
  1295. if (n <= 0) {
  1296. break;
  1297. }
  1298. for (int i = 0; i < n; i++) { // for_1
  1299. char c = buf[i];
  1300. if (c == '\004') /* C-d */
  1301. exit(0);
  1302. if (!arepl_readline(async_repl, c, line, sizeof(line)))
  1303. continue; // continue to for_1
  1304.  
  1305. int len = strlen(line);
  1306. line[--len] = '\0'; // remove trailing \n
  1307.  
  1308. if (TalkingTo != TALK_TYPE_NULL && line[0] != '/') { // if talking to someone, just print the msg out.
  1309. struct ChatHist **hp = get_current_histp();
  1310. if (!hp) {
  1311. ERROR(
  1312. "! You are not talking to someone. use `/go` to return to cmd mode");
  1313. continue; // continue to for_1
  1314. }
  1315. char *msg = genmsg(hp, SELF_MSG_PREFIX "%.*s", getftime(),
  1316. self.name, len, line);
  1317. PRINT("%s", msg);
  1318. switch (INDEX_TO_TYPE(TalkingTo)) {
  1319. case TALK_TYPE_FRIEND:
  1320. tox_friend_send_message(tox, INDEX_TO_NUM(TalkingTo),
  1321. TOX_MESSAGE_TYPE_NORMAL, (uint8_t*) line,
  1322. strlen(line), NULL);
  1323. continue; // continue to for_1
  1324. case TALK_TYPE_GROUP:
  1325. tox_conference_send_message(tox, INDEX_TO_NUM(TalkingTo),
  1326. TOX_MESSAGE_TYPE_NORMAL, (uint8_t*) line,
  1327. strlen(line), NULL);
  1328. continue; // continue to for_1
  1329. }
  1330. }
  1331.  
  1332. PRINT(CMD_MSG_PREFIX "%s", line); // take this input line as a command.
  1333. if (len == 0)
  1334. continue; // continue to for_1. ignore empty line
  1335.  
  1336. if (line[0] == '/') {
  1337. char *l = line + 1; // skip leading '/'
  1338. char *cmdname = poptok(&l);
  1339. struct Command *cmd = NULL;
  1340. for (int j = 0; j < COMMAND_LENGTH; j++) { // for_2
  1341. if (strcmp(commands[j].name, cmdname) == 0) {
  1342. cmd = &commands[j];
  1343. break; // break for_2
  1344. }
  1345. }
  1346. if (cmd) {
  1347. char *tokens[cmd->narg];
  1348. int ntok = 0;
  1349. for (; l != NULL && ntok != cmd->narg; ntok++) {
  1350. // if it's the last arg, then take the rest line.
  1351. char *tok = (ntok == cmd->narg - 1) ? l : poptok(&l);
  1352. tokens[ntok] = tok;
  1353. }
  1354. if (ntok
  1355. < cmd->narg
  1356. - (cmd->narg >= COMMAND_ARGS_REST ?
  1357. COMMAND_ARGS_REST :
  1358. 0)) {
  1359. WARN("Wrong number of cmd args");
  1360. } else {
  1361. cmd->handler(ntok, tokens);
  1362. if (SAVEDATA_AFTER_COMMAND)
  1363. update_savedata_file();
  1364. }
  1365. continue; // continue to for_1
  1366. }
  1367. }
  1368.  
  1369. WARN(
  1370. "! Invalid command, use `/help` to get list of available commands.");
  1371. } // end for_1
  1372. } // end while
  1373. arepl_reprint(async_repl);
  1374. }
  1375.  
  1376. int main(int argc, char **argv) {
  1377. if (argc == 2 && strcmp(argv[1], "--help") == 0) {
  1378. fputs("Usage: minitox\n", stdout);
  1379. fputs("\n", stdout);
  1380. fputs("Minitox does not take any arguments.\n", stdout);
  1381. return 0;
  1382. }
  1383.  
  1384. fputs("Type `/guide` to print the guide.\n", stdout);
  1385. fputs("Type `/help` to print command list.\n\n", stdout);
  1386.  
  1387. setup_arepl();
  1388. setup_tox();
  1389.  
  1390.  
  1391. // retrieve static secret key via tox_self_get_secret_key()
  1392. uint8_t secret_key[TOX_SECRET_KEY_SIZE];
  1393. tox_self_get_secret_key(tox, secret_key);
  1394. char *hex_sk = bin2hex(secret_key, sizeof(secret_key));
  1395. PRINT("%-15s%s", "Tox static secret key: ", hex_sk);
  1396.  
  1397. // TODO how to print dht secret key?
  1398. // tox->m->dht -> laut DHT.h
  1399. // TODO Zoff says just call the function und nicht mal header include notwendig?
  1400. uint8_t dht_private_key[TOX_SECRET_KEY_SIZE];
  1401. memcpy(dht_private_key, dht_get_self_public_key(tox->m->dht), TOX_SECRET_KEY_SIZE);
  1402. char *hex_dht_sk = bin2hex(dht_private_key, sizeof(dht_private_key));
  1403. PRINT("%-15s%s", "Tox DHT secret key: ", hex_dht_sk);
  1404.  
  1405. INFO("* Waiting to be online ...");
  1406.  
  1407. uint32_t msecs = 0;
  1408. while (1) {
  1409. if (msecs >= AREPL_INTERVAL) {
  1410. msecs = 0;
  1411. repl_iterate();
  1412. }
  1413. tox_iterate(tox, NULL);
  1414. uint32_t v = tox_iteration_interval(tox);
  1415. msecs += v;
  1416.  
  1417. struct timespec pause;
  1418. pause.tv_sec = 0;
  1419. pause.tv_nsec = v * 1000 * 1000;
  1420. nanosleep(&pause, NULL);
  1421. }
  1422.  
  1423. return 0;
  1424. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement