From 2ec180789b9d0e20293f23e2119cd57c288ac293 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Mon, 11 Jul 2016 17:42:57 -0400 Subject: [PATCH 01/23] Fix crash on AV error call state during active call --- src/audio_call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio_call.c b/src/audio_call.c index 2fd4024..f238460 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -228,7 +228,7 @@ int stop_transmission(Call *call, uint32_t friend_number) if ( call->ttas ) { TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK; - if ( CallControl.call_state != TOXAV_FRIEND_CALL_STATE_FINISHED ) + if ( CallControl.call_state > TOXAV_FRIEND_CALL_STATE_FINISHED ) toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error); if ( error == TOXAV_ERR_CALL_CONTROL_OK ) { From d3effa26b5395a8ce8739befea8490d332ba3ab4 Mon Sep 17 00:00:00 2001 From: Marvin Ewald Date: Sun, 1 Nov 2015 19:53:31 +0100 Subject: [PATCH 02/23] Add options to enable terminal bell on certain events Some terminals can mark the terminal window as urgent on bell. This is useful for window managers that provide a shortcut to jump to an urgent client. --- src/chat.c | 26 +++++++++++++++----------- src/groupchat.c | 4 ++-- src/notify.c | 2 +- src/settings.c | 26 ++++++++++++++++++++++++++ src/settings.h | 6 ++++++ 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/chat.c b/src/chat.c index cdebf8d..04e4901 100644 --- a/src/chat.c +++ b/src/chat.c @@ -160,9 +160,11 @@ static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const cha write_to_log(msg, nick, ctx->log, false); if (self->active_box != -1) - box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "%s", msg); + box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + self->active_box, "%s", msg); else - box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, nick, "%s", msg); + box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + &self->active_box, nick, "%s", msg); } static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char *action, size_t len, @@ -174,9 +176,11 @@ static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char write_to_log(action, nick, ctx->log, true); if (self->active_box != -1) - box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action ); + box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + self->active_box, "* %s %s", nick, action ); else - box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action ); + box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + &self->active_box, self->name, "* %s %s", nick, action ); } static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len) @@ -454,7 +458,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint char progline[MAX_STR_SIZE]; init_progress_bar(progline); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); - sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL); + sound_notify(self, silent, NT_NOFOCUS | user_settings->beep_on_filetrans_accept | NT_WNDALERT_2, NULL); ft->line_id = self->chatwin->hst->line_end->id + 2; } else if (ft->state == FILE_TRANSFER_PAUSED) { /* transfer is resumed */ ft->state = FILE_TRANSFER_STARTED; @@ -596,11 +600,11 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL); if (self->active_box != -1) - box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box, - "Incoming file: %s", filename ); + box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->beep_on_filetrans, + self->active_box, "Incoming file: %s", filename ); else - box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name, - "Incoming file: %s", filename ); + box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->beep_on_filetrans, + &self->active_box, self->name, "Incoming file: %s", filename ); } static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t type, const char *group_pub_key, @@ -623,7 +627,7 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui Friends.list[friendnumber].group_invite.length = length; Friends.list[friendnumber].group_invite.type = type; - sound_notify(self, generic_message, NT_WNDALERT_2, NULL); + sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->beep_on_invite, NULL); char name[TOX_MAX_NAME_LENGTH]; get_nick_truncate(m, name, friendnumber); @@ -651,7 +655,7 @@ void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); if (self->ringing_sound == -1) - sound_notify(self, call_incoming, NT_LOOP, &self->ringing_sound); + sound_notify(self, call_incoming, NT_LOOP | user_settings->beep_on_invite, &self->ringing_sound); if (self->active_box != -1) box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!"); diff --git a/src/groupchat.c b/src/groupchat.c index 1c07981..c481f6d 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -258,7 +258,7 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int /* Only play sound if mentioned by someone else */ if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { - sound_notify(self, generic_message, NT_WNDALERT_0, NULL); + sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->beep_on_message, NULL); if (self->active_box != -1) box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg); @@ -296,7 +296,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p selfnick[n_len] = '\0'; if (strcasestr(action, selfnick)) { - sound_notify(self, generic_message, NT_WNDALERT_0, NULL); + sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->beep_on_message, NULL); if (self->active_box != -1) box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action ); diff --git a/src/notify.c b/src/notify.c index 093ad95..2d2cec0 100644 --- a/src/notify.c +++ b/src/notify.c @@ -472,7 +472,7 @@ int play_notify_sound(Notification notif, uint64_t flags) int rc = -1; if (flags & NT_BEEP) beep(); - else if (notif != silent) { + if (notif != silent) { if ( !Control.poll_active || !Control.sounds[notif] ) return -1; diff --git a/src/settings.c b/src/settings.c index 75ecc80..1d82e39 100644 --- a/src/settings.c +++ b/src/settings.c @@ -51,6 +51,10 @@ static struct ui_strings { const char* timestamp_format; const char* log_timestamp_format; const char* alerts; + const char* beep_on_message; + const char* beep_on_filetrans; + const char* beep_on_filetrans_accept; + const char* beep_on_invite; const char* native_colors; const char* autolog; const char* history_size; @@ -73,6 +77,10 @@ static struct ui_strings { "timestamp_format", "log_timestamp_format", "alerts", + "beep_on_message", + "beep_on_filetrans", + "beep_on_filetrans_accept", + "beep_on_invite", "native_colors", "autolog", "history_size", @@ -96,6 +104,10 @@ static void ui_defaults(struct user_settings* settings) settings->autolog = AUTOLOG_OFF; settings->alerts = ALERTS_ENABLED; + settings->beep_on_message = 0; + settings->beep_on_filetrans = 0; + settings->beep_on_filetrans_accept = 0; + settings->beep_on_invite = 0; settings->colour_theme = DFLT_COLS; settings->history_size = 700; settings->show_typing_self = SHOW_TYPING_ON; @@ -319,6 +331,20 @@ int settings_load(struct user_settings *s, const char *patharg) } config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts); + + if (config_setting_lookup_bool(setting, ui_strings.beep_on_message, &s->beep_on_message)) { + s->beep_on_message = s->beep_on_message ? NT_BEEP : 0; + } + if (config_setting_lookup_bool(setting, ui_strings.beep_on_filetrans, &s->beep_on_filetrans)) { + s->beep_on_filetrans = s->beep_on_filetrans ? NT_BEEP : 0; + } + if (config_setting_lookup_bool(setting, ui_strings.beep_on_filetrans_accept, &s->beep_on_filetrans_accept)) { + s->beep_on_filetrans_accept = s->beep_on_filetrans_accept ? NT_BEEP : 0; + } + if (config_setting_lookup_bool(setting, ui_strings.beep_on_invite, &s->beep_on_invite)) { + s->beep_on_invite = s->beep_on_invite ? NT_BEEP : 0; + } + config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog); config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme); config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); diff --git a/src/settings.h b/src/settings.h index 53418af..d39203f 100644 --- a/src/settings.h +++ b/src/settings.h @@ -37,6 +37,12 @@ struct user_settings { int autolog; /* boolean */ int alerts; /* boolean */ + /* boolean (is set to NT_BEEP or 0 after loading) */ + int beep_on_message; + int beep_on_filetrans; + int beep_on_filetrans_accept; + int beep_on_invite; + int timestamps; /* boolean */ char timestamp_format[TIME_STR_SIZE]; char log_timestamp_format[TIME_STR_SIZE]; From 9aedcf7753d71d82e3ed2ce2779c08a3392c0ce4 Mon Sep 17 00:00:00 2001 From: Marvin Ewald Date: Sun, 17 Jul 2016 19:16:46 +0200 Subject: [PATCH 03/23] Update docs --- doc/toxic.conf.5 | 20 ++++++++++++++++++++ doc/toxic.conf.5.asc | 12 ++++++++++++ misc/toxic.conf.example | 12 ++++++++++++ 3 files changed, 44 insertions(+) diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index 9a1f15e..3f81cae 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -88,6 +88,26 @@ Time format string for logging enclosed by double quotes\&. See Enable or disable terminal alerts on events\&. true or false .RE .PP +\fBbeep_on_message\fR +.RS 4 +Beep when receiving a message\&. true or false +.RE +.PP +\fBbeep_on_filetrans\fR +.RS 4 +Beep when receiving a filetransfer\&. true or false +.RE +.PP +\fBbeep_on_filetrans_accept\fR +.RS 4 +Beep when a filetransfer was accepted\&. true or false +.RE +.PP +\fBbeep_on_invite\fR +.RS 4 +Beep when receiving a group/call invite\&. true or false +.RE +.PP \fBnative_colors\fR .RS 4 Select between native terminal colors and toxic color theme\&. true or false diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index a6c70db..6f2c87c 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -57,6 +57,18 @@ OPTIONS *alerts*;; Enable or disable terminal alerts on events. true or false + *beep_on_message* + Beep when receiving a message. true or false + + *beep_on_filetrans* + Beep when receiving a filetransfer. true or false + + *beep_on_filetrans_accept* + Beep when a filetransfer was accepted. true or false + + *beep_on_invite* + Beep when receiving a group/call invite. true or false + *native_colors*;; Select between native terminal colors and toxic color theme. true or false diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index f6302b4..3923482 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -8,6 +8,18 @@ ui = { // true to enable terminal alerts on messages, false to disable alerts=true; + // Beep when receiving a message + beep_on_message=true + + // Beep when receiving a filetransfer + beep_on_filetrans=true + + // Don't beep when a filetransfer was accepted + beep_on_filetrans_accept=false + + // Beep when receiving a group/call invite + beep_on_invite=true + // true to use native terminal colours, false to use toxic default colour theme native_colors=false; From f893dd755fb01e13174e890f17b31fb6f93632c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20Priv=C3=A9?= Date: Tue, 19 Jul 2016 00:06:52 +0100 Subject: [PATCH 04/23] Added a changelog to the project --- CHANGELOG.md | 503 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c275bb0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,503 @@ +# Change Log + +## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD) + +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD) + +**Closed issues:** + +- How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391) +- Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390) +- Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389) +- ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388) +- Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386) +- Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384) +- Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381) +- How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377) +- DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375) +- Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372) +- Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371) + +**Merged pull requests:** + +- Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe)) +- Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin)) +- sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer)) +- Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad)) +- Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias)) +- update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42)) +- Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong)) + +## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0) + +**Implemented enhancements:** + +- /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326) + +**Fixed bugs:** + +- Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279) +- Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275) +- Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258) + +**Closed issues:** + +- Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364) +- libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363) +- Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360) +- "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359) +- Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358) +- Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355) +- toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354) +- Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352) +- Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349) +- "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348) +- Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339) +- Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299) +- A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191) +- fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31) + +**Merged pull requests:** + +- Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun)) +- Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann)) + +## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1) + +**Closed issues:** + +- \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344) +- Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343) +- Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341) +- Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335) +- tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333) +- Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328) +- tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327) +- Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324) +- Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320) +- Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319) +- Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307) + +**Merged pull requests:** + +- fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret)) +- Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony)) +- Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry)) +- travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89)) +- Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89)) +- Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89)) +- Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89)) + +## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0) + +**Closed issues:** + +- Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311) +- Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295) +- openalut [\#287](https://github.com/JFreegman/toxic/issues/287) +- brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286) +- Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285) +- Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282) +- makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281) +- Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276) +- Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270) +- Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265) +- toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261) +- asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260) +- これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259) +- Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245) +- toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244) +- Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145) + +**Merged pull requests:** + +- README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89)) +- Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89)) +- Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89)) +- Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and)) +- added tab autocomplete for "/status o" =\> "/status online", etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven)) +- Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89)) +- Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng)) +- Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc)) +- README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89)) +- Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89)) +- travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89)) +- Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras)) +- Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc)) +- Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy)) +- Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89)) +- Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok)) +- Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras)) +- Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras)) +- Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence)) +- Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler)) +- Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888)) +- Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4)) +- Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89)) +- Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89)) +- Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89)) +- Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89)) +- Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89)) +- fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc)) +- Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok)) +- Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89)) + +## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2) + +**Closed issues:** + +- Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254) +- toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253) +- reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251) +- Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250) +- Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249) +- How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247) + +**Merged pull requests:** + +- Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89)) +- rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost)) +- Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras)) +- Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89)) + +## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1) + +**Closed issues:** + +- Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233) + +**Merged pull requests:** + +- Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89)) +- Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios)) +- Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89)) +- Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios)) +- Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios)) + +## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0) + +**Closed issues:** + +- 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232) +- Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228) +- Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227) +- Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225) +- MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222) +- multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221) +- openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219) +- build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216) +- UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171) +- Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115) + +**Merged pull requests:** + +- Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89)) +- Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89)) +- Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc)) +- toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89)) +- Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89)) +- Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc)) +- new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc)) +- toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc)) + +## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7) + +**Fixed bugs:** + +- Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106) + +**Closed issues:** + +- cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214) +- Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202) +- RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201) +- Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197) +- toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189) +- Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144) +- Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138) + +**Merged pull requests:** + +- Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost)) +- Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon)) +- Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost)) +- Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost)) +- Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89)) +- Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89)) +- Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163)) +- Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice)) +- Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok)) +- Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok)) +- Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost)) +- Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost)) +- README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89)) + +## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23) +[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6) + +**Implemented enhancements:** + +- "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192) + +**Closed issues:** + +- Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196) +- Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186) +- hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185) +- No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184) +- valgrind [\#178](https://github.com/JFreegman/toxic/issues/178) +- Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167) +- Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131) +- Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108) + +**Merged pull requests:** + +- Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89)) +- Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89)) +- Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost)) +- Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89)) +- Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost)) + +## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5) + +**Closed issues:** + +- building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177) +- Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175) +- Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173) +- Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172) +- Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163) +- new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160) +- new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159) +- new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158) +- Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143) +- Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125) +- Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118) +- Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101) + +**Merged pull requests:** + +- Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost)) +- Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89)) +- Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89)) +- Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89)) +- Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost)) +- Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89)) +- Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89)) +- Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost)) +- Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89)) +- Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89)) +- Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89)) +- Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate)) +- README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89)) +- Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89)) +- Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost)) +- Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost)) +- Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89)) +- Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89)) +- Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost)) + +## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1) + +**Closed issues:** + +- Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140) +- compiling error [\#139](https://github.com/JFreegman/toxic/issues/139) +- Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137) +- build fail [\#124](https://github.com/JFreegman/toxic/issues/124) +- Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120) + +**Merged pull requests:** + +- Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld)) +- Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld)) + +## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0) + +**Implemented enhancements:** + +- Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74) +- Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68) + +**Fixed bugs:** + +- Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84) +- Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71) + +**Closed issues:** + +- Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136) +- jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133) +- build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129) +- Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127) +- Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123) +- Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116) +- Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111) +- collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105) +- Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104) +- Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98) + +**Merged pull requests:** + +- Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok)) +- Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy)) +- Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost)) +- Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize)) +- Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff)) +- Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff)) +- Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost)) +- Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili)) +- Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy)) +- Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism)) +- allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman)) +- Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost)) +- Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost)) +- Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy)) +- Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld)) +- Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld)) +- Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost)) + +## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1) + +**Merged pull requests:** + +- SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin)) + +## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0) + +**Fixed bugs:** + +- SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89) + +**Merged pull requests:** + +- Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost)) +- This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost)) +- Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012)) +- Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost)) +- Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012)) +- Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able)) +- Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor)) + +## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7) + +**Closed issues:** + +- Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81) +- Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73) + +**Merged pull requests:** + +- down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng)) +- Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric)) + +## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1) + +## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23) +[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6) + +## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22) +[Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5) + +**Fixed bugs:** + +- Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44) + +**Closed issues:** + +- Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76) +- Segfault [\#75](https://github.com/JFreegman/toxic/issues/75) +- Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72) +- binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66) +- symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54) + +**Merged pull requests:** + +- ncurses libraries README note [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng)) +- umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy)) +- Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize)) +- Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize)) +- api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa)) +- Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize)) +- Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName)) +- Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize)) +- Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize)) + +## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28) +**Implemented enhancements:** + +- Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman)) +- Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName)) +- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) +- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) +- if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) +- truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman)) +- Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman)) +- Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman)) +- implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman)) +- Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman)) +- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) +- refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine)) +- properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman)) +- implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman)) +- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) +- Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles)) +- Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld)) +- Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism)) +- Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn)) +- Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld)) + +**Fixed bugs:** + +- Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012)) +- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName)) +- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman)) +- Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism)) +- made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman)) +- if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName)) +- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman)) +- fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman)) +- Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles)) +- fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93)) +- Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld)) +- Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld)) +- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld)) +- Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles)) +- Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld)) +- warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110)) + +**Merged pull requests:** + +- Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize)) +- Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize)) +- fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman)) +- Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras)) +- Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras)) +- Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman)) +- Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom)) +- Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th)) +- set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman)) +- moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman)) +- Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman)) +- Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName)) +- Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman)) +- Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent)) +- Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld)) + + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file From 5b1b420ac0dce4768e473fd11e4b2de3f1492638 Mon Sep 17 00:00:00 2001 From: Marvin Ewald Date: Thu, 21 Jul 2016 11:25:40 +0200 Subject: [PATCH 05/23] Add further explanation to the beep_on* options This also renames beep_on* to bell_on*. --- doc/toxic.conf.5 | 46 ++++++++++++++++++++++------------------- doc/toxic.conf.5.asc | 31 +++++++++++++++------------ misc/toxic.conf.example | 18 ++++++++-------- src/chat.c | 18 ++++++++-------- src/groupchat.c | 4 ++-- src/settings.c | 40 +++++++++++++++++------------------ src/settings.h | 8 +++---- 7 files changed, 87 insertions(+), 78 deletions(-) diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index 3f81cae..40c1d75 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -85,27 +85,7 @@ Time format string for logging enclosed by double quotes\&. See .PP \fBalerts\fR .RS 4 -Enable or disable terminal alerts on events\&. true or false -.RE -.PP -\fBbeep_on_message\fR -.RS 4 -Beep when receiving a message\&. true or false -.RE -.PP -\fBbeep_on_filetrans\fR -.RS 4 -Beep when receiving a filetransfer\&. true or false -.RE -.PP -\fBbeep_on_filetrans_accept\fR -.RS 4 -Beep when a filetransfer was accepted\&. true or false -.RE -.PP -\fBbeep_on_invite\fR -.RS 4 -Beep when receiving a group/call invite\&. true or false +Enable or disable acoustic alerts on events\&. true or false .RE .PP \fBnative_colors\fR @@ -172,6 +152,30 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or .RS 4 Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. .RE +.PP +The following options control whether to output a terminal bell on certain events\&. +.br +Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. These options don't affect the "alerts" option\&. +.PP +\fBbell_on_message\fR +.RS 4 +Enable/Disable the terminal bell when receiving a message\&. true or false +.RE +.PP +\fBbell_on_filetrans\fR +.RS 4 +Enable/Disable the terminal bell when receiving a filetransfer\&. true or false +.RE +.PP +\fBbell_on_filetrans_accept\fR +.RS 4 +Enable/Disable the terminal bell when a filetransfer was accepted\&. true or false +.RE +.PP +\fBbell_on_invite\fR +.RS 4 +Enable/Disable the terminal bell when receiving a group/call invite\&. true or false +.RE .RE .PP \fBaudio\fR diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index 6f2c87c..8e0b22f 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -55,19 +55,7 @@ OPTIONS See *date*(1) *alerts*;; - Enable or disable terminal alerts on events. true or false - - *beep_on_message* - Beep when receiving a message. true or false - - *beep_on_filetrans* - Beep when receiving a filetransfer. true or false - - *beep_on_filetrans_accept* - Beep when a filetransfer was accepted. true or false - - *beep_on_invite* - Beep when receiving a group/call invite. true or false + Enable or disable acoustic alerts on events. true or false *native_colors*;; Select between native terminal colors and toxic color theme. true or false @@ -112,6 +100,23 @@ OPTIONS detach. When attaching, the status message is set back to the original value. + The following options control whether to output a terminal bell on certain events. + Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window. + These options don't affect the "alerts" option. + + *bell_on_message* + Enable/Disable the terminal bell when receiving a message. true or false + + *bell_on_filetrans* + Enable/Disable the terminal bell when receiving a filetransfer. true or false + + *bell_on_filetrans_accept* + Enable/Disable the terminal bell when a filetransfer was accepted. true or false + + *bell_on_invite* + Enable/Disable the terminal bell when receiving a group/call invite. true or false + + *audio*:: Configuration related to audio devices. diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index 3923482..c162cdf 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -5,20 +5,20 @@ ui = { // true to enable timestamps, false to disable timestamps=true; - // true to enable terminal alerts on messages, false to disable + // true to enable acoustic alerts on messages, false to disable alerts=true; - // Beep when receiving a message - beep_on_message=true + // Output a bell when receiving a message (see manpage) + bell_on_message=true - // Beep when receiving a filetransfer - beep_on_filetrans=true + // Output a bell when receiving a filetransfer (see manpage) + bell_on_filetrans=true - // Don't beep when a filetransfer was accepted - beep_on_filetrans_accept=false + // Don't output a bell when a filetransfer was accepted (see manpage) + bell_on_filetrans_accept=false - // Beep when receiving a group/call invite - beep_on_invite=true + // Output a bell when receiving a group/call invite (see manpage) + bell_on_invite=true // true to use native terminal colours, false to use toxic default colour theme native_colors=false; diff --git a/src/chat.c b/src/chat.c index 04e4901..3df5288 100644 --- a/src/chat.c +++ b/src/chat.c @@ -160,10 +160,10 @@ static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const cha write_to_log(msg, nick, ctx->log, false); if (self->active_box != -1) - box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, self->active_box, "%s", msg); else - box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, &self->active_box, nick, "%s", msg); } @@ -176,10 +176,10 @@ static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char write_to_log(action, nick, ctx->log, true); if (self->active_box != -1) - box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, self->active_box, "* %s %s", nick, action ); else - box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->beep_on_message, + box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, &self->active_box, self->name, "* %s %s", nick, action ); } @@ -458,7 +458,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint char progline[MAX_STR_SIZE]; init_progress_bar(progline); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); - sound_notify(self, silent, NT_NOFOCUS | user_settings->beep_on_filetrans_accept | NT_WNDALERT_2, NULL); + sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL); ft->line_id = self->chatwin->hst->line_end->id + 2; } else if (ft->state == FILE_TRANSFER_PAUSED) { /* transfer is resumed */ ft->state = FILE_TRANSFER_STARTED; @@ -600,10 +600,10 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL); if (self->active_box != -1) - box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->beep_on_filetrans, + box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans, self->active_box, "Incoming file: %s", filename ); else - box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->beep_on_filetrans, + box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans, &self->active_box, self->name, "Incoming file: %s", filename ); } @@ -627,7 +627,7 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui Friends.list[friendnumber].group_invite.length = length; Friends.list[friendnumber].group_invite.type = type; - sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->beep_on_invite, NULL); + sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL); char name[TOX_MAX_NAME_LENGTH]; get_nick_truncate(m, name, friendnumber); @@ -655,7 +655,7 @@ void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); if (self->ringing_sound == -1) - sound_notify(self, call_incoming, NT_LOOP | user_settings->beep_on_invite, &self->ringing_sound); + sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound); if (self->active_box != -1) box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!"); diff --git a/src/groupchat.c b/src/groupchat.c index c481f6d..0e58cf3 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -258,7 +258,7 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int /* Only play sound if mentioned by someone else */ if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { - sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->beep_on_message, NULL); + sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL); if (self->active_box != -1) box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg); @@ -296,7 +296,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p selfnick[n_len] = '\0'; if (strcasestr(action, selfnick)) { - sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->beep_on_message, NULL); + sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL); if (self->active_box != -1) box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action ); diff --git a/src/settings.c b/src/settings.c index 1d82e39..cef92a2 100644 --- a/src/settings.c +++ b/src/settings.c @@ -51,10 +51,10 @@ static struct ui_strings { const char* timestamp_format; const char* log_timestamp_format; const char* alerts; - const char* beep_on_message; - const char* beep_on_filetrans; - const char* beep_on_filetrans_accept; - const char* beep_on_invite; + const char* bell_on_message; + const char* bell_on_filetrans; + const char* bell_on_filetrans_accept; + const char* bell_on_invite; const char* native_colors; const char* autolog; const char* history_size; @@ -77,10 +77,10 @@ static struct ui_strings { "timestamp_format", "log_timestamp_format", "alerts", - "beep_on_message", - "beep_on_filetrans", - "beep_on_filetrans_accept", - "beep_on_invite", + "bell_on_message", + "bell_on_filetrans", + "bell_on_filetrans_accept", + "bell_on_invite", "native_colors", "autolog", "history_size", @@ -104,10 +104,10 @@ static void ui_defaults(struct user_settings* settings) settings->autolog = AUTOLOG_OFF; settings->alerts = ALERTS_ENABLED; - settings->beep_on_message = 0; - settings->beep_on_filetrans = 0; - settings->beep_on_filetrans_accept = 0; - settings->beep_on_invite = 0; + settings->bell_on_message = 0; + settings->bell_on_filetrans = 0; + settings->bell_on_filetrans_accept = 0; + settings->bell_on_invite = 0; settings->colour_theme = DFLT_COLS; settings->history_size = 700; settings->show_typing_self = SHOW_TYPING_ON; @@ -332,17 +332,17 @@ int settings_load(struct user_settings *s, const char *patharg) config_setting_lookup_bool(setting, ui_strings.alerts, &s->alerts); - if (config_setting_lookup_bool(setting, ui_strings.beep_on_message, &s->beep_on_message)) { - s->beep_on_message = s->beep_on_message ? NT_BEEP : 0; + if (config_setting_lookup_bool(setting, ui_strings.bell_on_message, &s->bell_on_message)) { + s->bell_on_message = s->bell_on_message ? NT_BEEP : 0; } - if (config_setting_lookup_bool(setting, ui_strings.beep_on_filetrans, &s->beep_on_filetrans)) { - s->beep_on_filetrans = s->beep_on_filetrans ? NT_BEEP : 0; + if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans, &s->bell_on_filetrans)) { + s->bell_on_filetrans = s->bell_on_filetrans ? NT_BEEP : 0; } - if (config_setting_lookup_bool(setting, ui_strings.beep_on_filetrans_accept, &s->beep_on_filetrans_accept)) { - s->beep_on_filetrans_accept = s->beep_on_filetrans_accept ? NT_BEEP : 0; + if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans_accept, &s->bell_on_filetrans_accept)) { + s->bell_on_filetrans_accept = s->bell_on_filetrans_accept ? NT_BEEP : 0; } - if (config_setting_lookup_bool(setting, ui_strings.beep_on_invite, &s->beep_on_invite)) { - s->beep_on_invite = s->beep_on_invite ? NT_BEEP : 0; + if (config_setting_lookup_bool(setting, ui_strings.bell_on_invite, &s->bell_on_invite)) { + s->bell_on_invite = s->bell_on_invite ? NT_BEEP : 0; } config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog); diff --git a/src/settings.h b/src/settings.h index d39203f..a195ad0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -38,10 +38,10 @@ struct user_settings { int alerts; /* boolean */ /* boolean (is set to NT_BEEP or 0 after loading) */ - int beep_on_message; - int beep_on_filetrans; - int beep_on_filetrans_accept; - int beep_on_invite; + int bell_on_message; + int bell_on_filetrans; + int bell_on_filetrans_accept; + int bell_on_invite; int timestamps; /* boolean */ char timestamp_format[TIME_STR_SIZE]; From f89638635a634db782ca9f6daeacb75f911ab9e1 Mon Sep 17 00:00:00 2001 From: Marin Ivanov Date: Wed, 27 Jul 2016 02:07:31 +0200 Subject: [PATCH 06/23] Remove help window flicker --- src/prompt.c | 2 +- src/windows.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/prompt.c b/src/prompt.c index 4fbc8c6..4b8c978 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -369,7 +369,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); wmove(self->window, y + 1, new_x); - wrefresh(self->window); + wnoutrefresh(self->window); if (self->help->active) help_onDraw(self); diff --git a/src/windows.c b/src/windows.c index 0116a5a..8a6c6c9 100644 --- a/src/windows.c +++ b/src/windows.c @@ -471,6 +471,11 @@ static void draw_window_tab(ToxWindow *toxwin) static void draw_bar(void) { + int y,x; + + // save current cursor position + getyx(active_window->window, y, x); + attron(COLOR_PAIR(BLUE)); mvhline(LINES - 2, 0, '_', COLS); attroff(COLOR_PAIR(BLUE)); @@ -508,6 +513,9 @@ static void draw_bar(void) attroff(A_BOLD); } + // restore cursor position after drawing + move(y, x); + refresh(); } From 94e026d114e1281a58219c28a7f2b1708431bfe5 Mon Sep 17 00:00:00 2001 From: Marin Ivanov Date: Wed, 27 Jul 2016 02:39:31 +0200 Subject: [PATCH 07/23] Remove chat infobox flicker --- src/chat.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/chat.c b/src/chat.c index 3df5288..b33c475 100644 --- a/src/chat.c +++ b/src/chat.c @@ -862,7 +862,7 @@ static void draw_infobox(ToxWindow *self) wprintw(infobox->win, "%.2f\n", infobox->vad_lvl); wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' '); - wrefresh(infobox->win); + wnoutrefresh(infobox->win); } #endif /* AUDIO */ @@ -1104,12 +1104,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m) int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); wmove(self->window, y + 1, new_x); - wrefresh(self->window); + wnoutrefresh(self->window); #ifdef AUDIO if (ctx->infobox.active) { draw_infobox(self); - wrefresh(self->window); } #endif From c425aa2f27987687b5a563c8fa56857aac41880f Mon Sep 17 00:00:00 2001 From: Marin Ivanov Date: Wed, 27 Jul 2016 18:06:33 +0200 Subject: [PATCH 08/23] Fixed input lag --- src/windows.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/windows.c b/src/windows.c index 8a6c6c9..5414b4b 100644 --- a/src/windows.c +++ b/src/windows.c @@ -533,6 +533,7 @@ void draw_active_window(Tox *m) touchwin(a->window); a->onDraw(a, m); + wrefresh(a->window); /* Handle input */ bool ltr; From 4019395f44a97fd5b021f2b98517ffe15e9ff6aa Mon Sep 17 00:00:00 2001 From: Marin Ivanov Date: Thu, 28 Jul 2016 17:47:06 +0200 Subject: [PATCH 09/23] Changed video get format to set format * Fixes issue #394 * This was obviously meant to set the video format. --- src/video_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_device.c b/src/video_device.c index 19b4508..2307049 100644 --- a/src/video_device.c +++ b/src/video_device.c @@ -362,7 +362,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - if( -1 == xioctl(device->fd, VIDIOC_G_FMT, &fmt) ) { + if( -1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt) ) { close(device->fd); free(device); unlock; From 379ad9e116770bde11f8c5233485312dc4fe12ad Mon Sep 17 00:00:00 2001 From: "Keegan Drake H.P" Date: Wed, 14 Sep 2016 05:43:10 -0500 Subject: [PATCH 10/23] Use ALC_ENUMERATE_ALL_EXT when available --- src/audio_device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/audio_device.c b/src/audio_device.c index c74e8ea..9e6c1e2 100644 --- a/src/audio_device.c +++ b/src/audio_device.c @@ -113,7 +113,11 @@ DeviceError init_devices() } size[output] = 0; - if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) { + if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) + stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); + else + stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); + if (stringed_device_list) { ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { From 1f02bb2be57f1a94ccfba26aff0a55b9a8ba0ff3 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Sat, 17 Sep 2016 14:26:23 -0400 Subject: [PATCH 11/23] Refactor DHT bootstrap code - Separate node list loading from connecting - Put code in its own source file - Rename a few functions --- Makefile | 2 +- src/bootstrap.c | 151 +++++++++++++++++++++++++++++++++++++ src/bootstrap.h | 32 ++++++++ src/toxic.c | 193 ++++-------------------------------------------- 4 files changed, 198 insertions(+), 180 deletions(-) create mode 100644 src/bootstrap.c create mode 100644 src/bootstrap.h diff --git a/Makefile b/Makefile index 6781c60..abb09ff 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ LDFLAGS = $(USER_LDFLAGS) OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o -OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o +OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o bootstrap.o # Check on wich system we are running UNAME_S = $(shell uname -s) diff --git a/src/bootstrap.c b/src/bootstrap.c new file mode 100644 index 0000000..adb4e76 --- /dev/null +++ b/src/bootstrap.c @@ -0,0 +1,151 @@ +/* bootstrap.c + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#include +#include +#include + +#include + +#include "line_info.h" +#include "windows.h" +#include "misc_tools.h" + + +extern struct arg_opts arg_opts; + + +/* Time to wait between bootstrap attempts */ +#define TRY_BOOTSTRAP_INTERVAL 5 + + +#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */ +#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */ +#define MAXNODES 50 +#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7) + +static struct toxNodes { + size_t lines; + char nodes[MAXNODES][NODELEN]; + uint16_t ports[MAXNODES]; + char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; +} toxNodes; + +/* Load the DHT nodelist to memory. + * + * Return 0 on success. + * Return -1 if nodelist file cannot be opened. + * Return -2 if nodelist file does not contain any valid node entries. + */ +int load_DHT_nodelist(void) +{ + + const char *filename = !arg_opts.nodes_path[0] ? PACKAGE_DATADIR "/DHTnodes" : arg_opts.nodes_path; + + FILE *fp = fopen(filename, "r"); + + if (fp == NULL) + return -1; + + char line[MAX_NODE_LINE]; + + while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) { + size_t line_len = strlen(line); + + if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) { + const char *name = strtok(line, " "); + const char *port_str = strtok(NULL, " "); + const char *key_ascii = strtok(NULL, " "); + + if (name == NULL || port_str == NULL || key_ascii == NULL) + continue; + + long int port = strtol(port_str, NULL, 10); + + if (port <= 0 || port > MAX_PORT_RANGE) + continue; + + size_t key_len = strlen(key_ascii); + size_t name_len = strlen(name); + + if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN) + continue; + + snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name); + toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0; + toxNodes.ports[toxNodes.lines] = port; + + /* remove possible trailing newline from key string */ + char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1]; + memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2); + key_len = TOX_PUBLIC_KEY_SIZE * 2; + real_ascii_key[key_len] = '\0'; + + if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) + continue; + + toxNodes.lines++; + } + } + + fclose(fp); + + if (toxNodes.lines == 0) + return -2; + + return 0; +} + +/* Connects to a random DHT node listed in the DHTnodes file. */ +static void DHT_bootstrap(Tox *m) +{ + if (toxNodes.lines == 0) { + return; + } + + size_t node = rand() % toxNodes.lines; + + TOX_ERR_BOOTSTRAP err; + tox_bootstrap(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + + if (err != TOX_ERR_BOOTSTRAP_OK) { + fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + } + + tox_add_tcp_relay(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + + if (err != TOX_ERR_BOOTSTRAP_OK) { + fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + } +} + +/* Manages connection to the Tox DHT network. */ +void do_tox_connection(Tox *m) +{ + static uint64_t last_bootstrap_time = 0; + bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE; + + if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) { + DHT_bootstrap(m); + last_bootstrap_time = get_unix_time(); + } +} diff --git a/src/bootstrap.h b/src/bootstrap.h new file mode 100644 index 0000000..f442ce2 --- /dev/null +++ b/src/bootstrap.h @@ -0,0 +1,32 @@ +/* bootstrap.h + * + * + * Copyright (C) 2014 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +/* Manages connection to the Tox DHT network. */ +void do_tox_connection(Tox *m); + +/* Load the DHT nodelist to memory. + * + * Return 0 on success. + * Return -1 if nodelist file cannot be opened. + * Return -2 if nodelist file does not contain any valid node entries. + */ +int load_DHT_nodelist(void); diff --git a/src/toxic.c b/src/toxic.c index 476b18c..ab56f5c 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -60,6 +60,7 @@ #include "execute.h" #include "term_mplex.h" #include "name_lookup.h" +#include "bootstrap.h" #ifdef X11 #include "xtra.h" @@ -280,153 +281,6 @@ static void print_init_messages(ToxWindow *toxwin) line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]); } -#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */ -#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */ -#define MAXNODES 50 -#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7) - -static struct toxNodes { - int lines; - char nodes[MAXNODES][NODELEN]; - uint16_t ports[MAXNODES]; - char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; -} toxNodes; - -static int load_nodelist(const char *filename) -{ - if (!filename) - return 1; - - FILE *fp = fopen(filename, "r"); - - if (fp == NULL) - return 1; - - char line[MAX_NODE_LINE]; - - while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) { - size_t line_len = strlen(line); - - if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) { - const char *name = strtok(line, " "); - const char *port_str = strtok(NULL, " "); - const char *key_ascii = strtok(NULL, " "); - - if (name == NULL || port_str == NULL || key_ascii == NULL) - continue; - - long int port = strtol(port_str, NULL, 10); - - if (port <= 0 || port > MAX_PORT_RANGE) - continue; - - size_t key_len = strlen(key_ascii); - size_t name_len = strlen(name); - - if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN) - continue; - - snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name); - toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0; - toxNodes.ports[toxNodes.lines] = port; - - /* remove possible trailing newline from key string */ - char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1]; - memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2); - key_len = TOX_PUBLIC_KEY_SIZE * 2; - real_ascii_key[key_len] = '\0'; - - if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) - continue; - - toxNodes.lines++; - } - } - - fclose(fp); - - if (toxNodes.lines < 1) - return 1; - - return 0; -} - -/* Bootstraps and adds as TCP relay. - * Returns 0 if both actions are successful. - * Returns -1 otherwise. - */ -int init_connection_helper(Tox *m, int line) -{ - TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err); - - if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]); - return -1; - } - - tox_add_tcp_relay(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err); - - if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]); - return -1; - } - - return 0; -} - -/* Connects to a random DHT node listed in the DHTnodes file - * - * return codes: - * 0: success - * 1: failed to open node file - * 2: no line of sufficient length in node file - * 3: failed to resolve name to IP - * 4: nodelist file contains no acceptable line - */ -static bool srvlist_loaded = false; - -#define NUM_INIT_NODES 5 - -int init_connection(Tox *m) -{ - if (toxNodes.lines > 0) { /* already loaded nodelist */ - init_connection_helper(m, rand() % toxNodes.lines); - return 0; - } - - /* only once: - * - load the nodelist - * - connect to "everyone" inside - */ - if (!srvlist_loaded) { - srvlist_loaded = true; - int res; - - if (!arg_opts.nodes_path[0]) - res = load_nodelist(PACKAGE_DATADIR "/DHTnodes"); - else - res = load_nodelist(arg_opts.nodes_path); - - if (res != 0) - return res; - - res = 3; - int i; - int n = MIN(NUM_INIT_NODES, toxNodes.lines); - - for (i = 0; i < n; ++i) { - if (init_connection_helper(m, rand() % toxNodes.lines) == 0) - res = 0; - } - - return res; - } - - /* empty nodelist file */ - return 4; -} - static void load_friendlist(Tox *m) { size_t i; @@ -873,30 +727,7 @@ static Tox *load_toxic(char *data_path) return m; } -#define TRY_BOOTSTRAP_INTERVAL 5 -static uint64_t last_bootstrap_time = 0; - -static void do_bootstrap(Tox *m) -{ - static int conn_err = 0; - - if (!timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) - return; - - if (tox_self_get_connection_status(m) != TOX_CONNECTION_NONE) - return; - - if (conn_err != 0) - return; - - last_bootstrap_time = get_unix_time(); - conn_err = init_connection(m); - - if (conn_err != 0) - line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Auto-connect failed with error code %d", conn_err); -} - -static void do_toxic(Tox *m, ToxWindow *prompt) +static void do_toxic(Tox *m) { pthread_mutex_lock(&Winthread.lock); update_unix_time(); @@ -907,7 +738,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt) } tox_iterate(m); - do_bootstrap(m); + do_tox_connection(m); pthread_mutex_unlock(&Winthread.lock); } @@ -1280,9 +1111,6 @@ int main(int argc, char **argv) bool datafile_exists = file_exists(DATA_FILE); - if (datafile_exists) - last_bootstrap_time = get_unix_time(); - if (!datafile_exists && !arg_opts.unencrypt_data) first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)"); else if (arg_opts.encrypt_data) @@ -1300,14 +1128,21 @@ int main(int argc, char **argv) if (settings_load(user_settings, p) == -1) queue_init_message("Failed to load user settings"); + int nodelist_ret = load_DHT_nodelist(); + + if (nodelist_ret != 0) { + queue_init_message("DHT nodelist failed to load (error %d). You can still connect manually with the /connect command.", nodelist_ret); + } + int nameserver_ret = name_lookup_init(); - if (nameserver_ret == -1) + if (nameserver_ret == -1) { queue_init_message("curl failed to initialize; name lookup service is disabled."); - else if (nameserver_ret == -2) + } else if (nameserver_ret == -2) { queue_init_message("Name lookup server list could not be found."); - else if (nameserver_ret == -3) + } else if (nameserver_ret == -3) { queue_init_message("Name lookup server list does not contain any valid entries."); + } #ifdef X11 if (init_xtra(DnD_callback) == -1) @@ -1379,7 +1214,7 @@ int main(int argc, char **argv) uint64_t last_save = (uint64_t) time(NULL); while (true) { - do_toxic(m, prompt); + do_toxic(m); uint64_t cur_time = get_unix_time(); From 5e20e6b27929509311504b1a1e1eb58d80714176 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Sat, 17 Sep 2016 21:38:32 -0400 Subject: [PATCH 12/23] Switch to using json DHT nodes file Parsing json manually like this is ugly, but this allows us to use the json formatted nodeslist file at nodes.tox.chat instead of having to update the list by hand. We could also potentially use curl to fetch the list and update it automatically. --- misc/DHTnodes | 39 +------------- src/bootstrap.c | 133 ++++++++++++++++++++++++++++++++--------------- src/bootstrap.h | 7 +-- src/misc_tools.c | 12 ++++- src/misc_tools.h | 6 +-- 5 files changed, 109 insertions(+), 88 deletions(-) diff --git a/misc/DHTnodes b/misc/DHTnodes index 4e65dfd..bdaefc6 100644 --- a/misc/DHTnodes +++ b/misc/DHTnodes @@ -1,38 +1 @@ -144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F -23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074 -195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354 -biribiri.org 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67 -46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A -178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B -130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F -104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57 -205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702 -198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F -80.232.246.79 33445 CF6CECA0A14A31717CC8501DA51BE27742B70746956E6676FF423A529F91ED5D -108.61.165.198 33445 8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832 -212.71.252.109 33445 C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819 -194.249.212.109 33445 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B -185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43 -192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F -46.101.197.175 443 CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707 -95.215.46.114 33445 5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23 -5.189.176.217 5190 2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F -148.251.23.146 2306 7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147 -104.223.122.15 33445 0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A -78.47.114.252 33445 1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976 -d4rk4.ru 1813 53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039 -81.4.110.149 33445 9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E -95.31.20.151 33445 9CA69BB74DE7C056D1CC6B16AB8A0A38725C0349D187D8996766958584D39340 -104.233.104.126 33445 EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414 -51.254.84.212 33445 AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D -home.vikingmakt.com.br 33445 188E072676404ED833A4E947DC1D223DF8EFEBE5F5258573A236573688FB9761 -5.135.59.163 33445 2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211 -185.58.206.164 33445 24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39 -188.244.38.183 33445 15A0F9684E2423F9F46CFA5A50B562AE42525580D840CC50E518192BF333EE38 -mrflibble.c4.ee 33445 FAAB17014F42F7F20949F61E55F66A73C230876812A9737F5F6D2DCE4D9E4207 -82.211.31.116 33445 AF97B76392A6474AF2FD269220FDCF4127D86A42EF3A242DF53A40A268A2CD7C -128.199.199.197 33445 B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09 -103.230.156.174 33445 5C4C7A60183D668E5BD8B3780D1288203E2F1BAE4EEF03278019E21F86174C1D -91.121.66.124 33445 4E3F7D37295664BBD0741B6DBCB6431D6CD77FC4105338C2FC31567BF5C8224A -92.54.84.70 33445 5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802 -tox1.privacydragon.me 33445 31910C0497D347FF160D6F3A6C0E317BAFA71E8E03BC4CBB2A185C9D4FB8B31E +{"last_scan":1474137109,"nodes":[{"ipv4":"tox.zodiaclabs.org","ipv6":"v6.tox.zodiaclabs.org","port":33445,"tcp_ports":[33445,3389],"public_key":"A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074","maintainer":"stal","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"I am the bone of my code;\nNIH is my body and str8c is my blood.\nI have overflowed a thousand buffers.\nUnknown to SIGSEGV, nor to abort();\nHave withstood pain to create fast programs;\nYet my code will always be unsafe.\nSo as I pray, rock solid straight C.","last_ping":1474137098},{"ipv4":"biribiri.org","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67","maintainer":"nurupo","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Welcome, stranger #3783. I'm up for 1d 14h 23m 12s, running since Sep 16 04:08:25 UTC. If I get outdated, please ping my maintainer at nurupo.contributions@gmail.com","last_ping":1474137098},{"ipv4":"178.62.250.138","ipv6":"2a03:b0c0:2:d0::16:1","port":33445,"tcp_ports":[3389,33445],"public_key":"788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B","maintainer":"Impyy","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Impy's kickin' bootstrap node","last_ping":1474137097},{"ipv4":"130.133.110.14","ipv6":"2001:6f8:1c3c:babe::14:1","port":33445,"tcp_ports":[33445],"public_key":"461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F","maintainer":"Manolis","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Spline tox bootstrap node","last_ping":1474137101},{"ipv4":"205.185.116.116","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"198.98.51.198","ipv6":"2605:6400:1:fed5:22:45af:ec10:f329","port":33445,"tcp_ports":[33445,3389,443],"public_key":"1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"139.162.217.110","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"0EEBE6304F4B3F6549F39A87FBB45751929F4833BA6AC5F35B6DFA79D01B4523","maintainer":"Kr9r0x","location":"GB","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137097},{"ipv4":"194.249.212.109","ipv6":"2001:1470:fbfe::109","port":33445,"tcp_ports":[3389,443,33445],"public_key":"3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B","maintainer":"fluke571","location":"SI","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"185.25.116.107","ipv6":"2a00:7a60:0:746b::3","port":33445,"tcp_ports":[33445,3389],"public_key":"DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43","maintainer":"MAH69K","location":"UA","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137102},{"ipv4":"192.99.168.140","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F","maintainer":"WIeschie","location":"CA","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"95.215.46.114","ipv6":"2a02:7aa0:1619::bdbd:17b8","port":33445,"tcp_ports":[33445,3389],"public_key":"5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23","maintainer":"Rotkaermota","location":"SE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137102},{"ipv4":"5.189.176.217","ipv6":"2a02:c200:1:10:3:1:605:1337","port":5190,"tcp_ports":[33445,5190,3389],"public_key":"2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F","maintainer":"tastytea","location":"DE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137101},{"ipv4":"148.251.23.146","ipv6":"2a01:4f8:201:8493::2","port":2306,"tcp_ports":[33445,2306],"public_key":"7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147","maintainer":"pucetox","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"by pucetox,\n sync your nodes here tox.0x10k.com/bootstrapd-conf , \n for communication: 1D1C0B992DEB6D7F18561176F7F5E572BCC7F2BA5CFA7E9E437B9134122CE96D906A6119F9D2","last_ping":1474137097},{"ipv4":"104.223.122.15","ipv6":"2607:ff48:aa81:800::35eb:1","port":33445,"tcp_ports":[3389,33445],"public_key":"0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A","maintainer":"ru_maniac","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox id for queries and general info: EBD2A7B649ABB10ED9F47E5113F04000F39D46F087CEB62FCCE1069471FD6915256D197F2A97","last_ping":1474137098},{"ipv4":"tox.verdict.gg","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976","maintainer":"Deliran","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Since 2015","last_ping":1474137098},{"ipv4":"d4rk4.ru","ipv6":"-","port":1813,"tcp_ports":[1813],"public_key":"53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039","maintainer":"D4rk4","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"TOX ID: 35EDC07AEB18B163E07EE33F6CDDA63969F394FF6A617CEAB22A7EBBEAAAF854C0EDFBD46898","last_ping":1474137102},{"ipv4":"81.4.110.149","ipv6":"2a00:d880:3:2::8bdc:f19","port":33445,"tcp_ports":[33445,3389],"public_key":"9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E","maintainer":"tibietigni","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Tibietigni.com node. Tox_id for communication: D36CC0B702621F48FDBC540A57124A744E5133C932E65ACCEBCABF2586A02455171717175989","last_ping":1474137097},{"ipv4":"95.31.20.151","ipv6":"-","port":33445,"tcp_ports":[3389],"public_key":"9CA69BB74DE7C056D1CC6B16AB8A0A38725C0349D187D8996766958584D39340","maintainer":"IgorNovgorodov","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"happy-new-year","last_ping":1474137101},{"ipv4":"51.254.84.212","ipv6":"2001:41d0:a:1a3b::18","port":33445,"tcp_ports":[3389,33445],"public_key":"AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D","maintainer":"a68366","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Since 26.12.2015","last_ping":1474137097},{"ipv4":"5.135.59.163","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211","maintainer":"Skey","location":"FR","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137097},{"ipv4":"185.58.206.164","ipv6":"2a02:f680:1:1100::3313","port":33445,"tcp_ports":[3389,33445],"public_key":"24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39","maintainer":"ru_maniac","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"please note: running on iphy's fork!\nmore info on the matter: goo.gl/Gz5KhK \u0026 goo.gl/i2TZJr\n\ntox id for queries and general info: EBD2A7B649ABB10ED9F47E5113F04000F39D46F087CEB62FCCE1069471FD6915256D197F2A97","last_ping":1474137098},{"ipv4":"128.199.199.197","ipv6":"2400:6180:0:d0::17a:a001","port":33445,"tcp_ports":[3389,33445],"public_key":"B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09","maintainer":"wiiaam","location":"SG","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"apt-get install antox","last_ping":1474137098},{"ipv4":"bootybay.club","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"0CB9D8D636F8E3D71CF44A3019408A846B7BEDFA2810853355DB58C0A36BFE38","maintainer":"AssPirate","location":"AU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"91.121.66.124","ipv6":"2001:41d0:1:757c::1","port":33445,"tcp_ports":[33445],"public_key":"4E3F7D37295664BBD0741B6DBCB6431D6CD77FC4105338C2FC31567BF5C8224A","maintainer":"h4nt3r","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox id for queries and general info: 3BC3C6875508A2EC86BE3E35E4FA9155E444CFA96671AE7D7D0D2585A0A5FA38E071A5E463E5","last_ping":1474137097},{"ipv4":"195.93.190.6","ipv6":"2a01:d0:ffff:a8a::2","port":33445,"tcp_ports":[33445],"public_key":"FB4CE0DDEFEED45F26917053E5D24BDDA0FA0A3D83A672A9DA2375928B37023D","maintainer":"strngr","location":"UA","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137105},{"ipv4":"loki.uplinklabs.net","ipv6":"loki.uplinklabs.net","port":33445,"tcp_ports":[3389,33445],"public_key":"1A56EA3EDF5DF4C0AEABBF3C2E4E603890F87E983CAC8A0D532A335F2C6E3E1F","maintainer":"AbacusAvenger","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"95.215.44.78","ipv6":"2a02:7aa0:1619::c6fe:d0cb","port":33445,"tcp_ports":[33445,3389],"public_key":"672DBE27B4ADB9D5FB105A6BB648B2F8FDB89B3323486A7A21968316E012023C","maintainer":"HooinKyoma","location":"SE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Thanx to Hooin Kyoma","last_ping":1474137098},{"ipv4":"tox.ntp.moscow","ipv6":"tox.ntp.moscow","port":33445,"tcp_ports":[33445,3389],"public_key":"80EF8660D9C5ACE1577BAB031375A0F08284CBFD9F810A857955DCC87377FC4D","maintainer":"abbat","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-easy-bootstrap","last_ping":1474137098},{"ipv4":"146.185.136.123","ipv6":"2a03:b0c0:0:1010::389:1","port":33445,"tcp_ports":[33445,443,3389],"public_key":"09993FAF174DFFDC515B398A2EFC5639C4E6D7B736FC864F89786B50EAF88C1A","maintainer":"LittleVulpix","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"LittleTox - your friendly neighbourhood tox node from the land of windmills!","last_ping":1474137097},{"ipv4":"163.172.136.118","ipv6":"2001:bc8:4400:2100::1c:50f","port":33445,"tcp_ports":[33445,3389],"public_key":"2C289F9F37C20D09DA83565588BF496FAB3764853FA38141817A72E3F18ACA0B","maintainer":"LittleVulpix","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"LittleTox - your friendly neighbourhood tox node!","last_ping":1474137097},{"ipv4":"37.97.185.116","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"E59A0E71ADA20D35BD1B0957059D7EF7E7792B3D680AE25C6F4DBBA09114D165","maintainer":"Yani","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Yani's node of pleasure and leisure","last_ping":1474137097},{"ipv4":"193.124.186.205","ipv6":"2a02:f680:1:1100::542a","port":5228,"tcp_ports":[443,3389,33445],"public_key":"9906D65F2A4751068A59D30505C5FC8AE1A95E0843AE9372EAFA3BAB6AC16C2C","maintainer":"Cactus","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"80.87.193.193","ipv6":"2a01:230:2:6::46a8","port":33445,"tcp_ports":[33445,3389],"public_key":"B38255EE4B054924F6D79A5E6E5889EC94B6ADF6FE9906F97A3D01E3D083223A","maintainer":"linxon","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Free Linxon's TOX Bootstrap node","last_ping":1474137098},{"ipv4":"initramfs.io","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25","maintainer":"initramfs","location":"TW","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"initramfs' Tox DHT Node","last_ping":1474137099},{"ipv4":"tox.neverlocate.me","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"49183DBF0E865713154069D1C7C7A2732ED78CF32C4D76AF5304FE31C5FEB81A","maintainer":"brandon","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137102},{"ipv4":"shigure.eve.moe","ipv6":"shigure.eve.moe","port":33445,"tcp_ports":[3389,33445],"public_key":"1A480A53FAF2CBBFCC382D786C43E69EEE23F22C7C23A7CFEB6180A373E23E54","maintainer":"EveNeko","location":"GB","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd@shigure.eve.moe","last_ping":1474137098},{"ipv4":"tox.deadteam.org","ipv6":"tox.deadteam.org","port":33445,"tcp_ports":[33445],"public_key":"C7D284129E83877D63591F14B3F658D77FF9BA9BA7293AEB2BDFBFE1A803AF47","maintainer":"DeadTeam","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Vive le TOX","last_ping":1474137101},{"ipv4":"prok.pw","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"69C3FEBB977687B64FA0213BDEB89A43463BB48DED288150CFFB6429EFF82436","maintainer":"Prototik","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Have a nice chatting!","last_ping":1474137098},{"ipv4":"maggie.prok.pw","ipv6":"maggie.prok.pw","port":33445,"tcp_ports":[33445,3389],"public_key":"B75583B6D967DB8AD7C6D3B6F9318194BCC79B2FEF18F69E2DF275B779E7AA30","maintainer":"Prototik","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Maggie wants a pacifier","last_ping":1474137101},{"ipv4":"108.61.165.198","ipv6":"2001:19f0:5000:8121:5054:ff:fe1c:9ded","port":33445,"tcp_ports":[],"public_key":"8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832","maintainer":"ray65536","location":"NL","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"Ray's Tox Node","last_ping":1474137101},{"ipv4":"92.54.84.70","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802","maintainer":"t3mp","location":"RU","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137102},{"ipv4":"srv1.ricin.im","ipv6":"-","port":1337,"tcp_ports":[],"public_key":"3651DAB570D7F60381F87B19D6935EE7F5FE01308DCE71C4B69993150C6A903C","maintainer":"Ricin.im","location":"FR","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"Ricin.im Tox node. Enjoy babes \u003c3","last_ping":1474137101},{"ipv4":"46.101.197.175","ipv6":"2a03:b0c0:3:d0::ac:5001","port":443,"tcp_ports":[443,33445,3389],"public_key":"CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707","maintainer":"clearmartin","location":"DE","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137105},{"ipv4":"toxnode.nek0.net","ipv6":"toxnode.nek0.net","port":33445,"tcp_ports":[3389,33445],"public_key":"20965721D32CE50C3E837DD75B33908B33037E6225110BFF209277AEAF3F9639","maintainer":"Phsm","location":"UA","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137107},{"ipv4":"sorunome.de","ipv6":"sorunome.de","port":33445,"tcp_ports":[33445,3389],"public_key":"02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46","maintainer":"Sorunome","location":"DE","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137105},{"ipv4":"hibiki.eve.moe","ipv6":"hibiki.eve.moe","port":33445,"tcp_ports":[33445],"public_key":"D3EB45181B343C2C222A5BCF72B760638E15ED87904625AAD351C594EEFAE03E","maintainer":"EveNeko","location":"US","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137098},{"ipv4":"46.229.52.198","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"813C8F4187833EF0655B10F7752141A352248462A567529A38B6BBF73E979307","maintainer":"Stranger","location":"UA","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137109},{"ipv4":"144.76.60.215","ipv6":"2a01:4f8:191:64d6::1","port":33445,"tcp_ports":[],"public_key":"04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F","maintainer":"sonOfRa","location":"DE","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"195.154.119.113","ipv6":"2001:bc8:3698:101::1","port":33445,"tcp_ports":[],"public_key":"E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354","maintainer":"Munrek","location":"FR","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"46.38.239.179","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A","maintainer":"MartinSchröder","location":"DE","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"104.167.101.29","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57","maintainer":"noisykeyboard","location":"CA","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"80.232.246.79","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"CF6CECA0A14A31717CC8501DA51BE27742B70746956E6676FF423A529F91ED5D","maintainer":"fUNKIAM","location":"LV","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"104.233.104.126","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414","maintainer":"wildermesser","location":"CA","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"home.vikingmakt.com.br","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"188E072676404ED833A4E947DC1D223DF8EFEBE5F5258573A236573688FB9761","maintainer":"umgeher","location":"BR","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"188.244.38.183","ipv6":"2001:470:de00:2:20c:29ff:fe68:354f","port":33445,"tcp_ports":[],"public_key":"15A0F9684E2423F9F46CFA5A50B562AE42525580D840CC50E518192BF333EE38","maintainer":"gem","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"mrflibble.c4.ee","ipv6":"2a02:16e0:0:12::4","port":33445,"tcp_ports":[],"public_key":"FAAB17014F42F7F20949F61E55F66A73C230876812A9737F5F6D2DCE4D9E4207","maintainer":"mrflibble","location":"GB","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"82.211.31.116","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"AF97B76392A6474AF2FD269220FDCF4127D86A42EF3A242DF53A40A268A2CD7C","maintainer":"Net.Verified","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"tox1.privacydragon.me","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"31910C0497D347FF160D6F3A6C0E317BAFA71E8E03BC4CBB2A185C9D4FB8B31E","maintainer":"PrivacyDragon","location":"US","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"zawertun.net","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5521952892FBD5C185DF7180DB4DEF69D7844DEEE79B1F75A634ED9DF656756E","maintainer":"ZaWertun","location":"NL","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"87.98.168.93","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"C3F6C06A624FAE086DA94604A7838DB495769807EC055FADA36EBF2D4484FB33","maintainer":"_kinka_","location":"ES","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"185.61.253.189","ipv6":"2a04:ee00:0:9:20c:29ff:fe27:ad96","port":33445,"tcp_ports":[],"public_key":"73EEBCB4CBBE56BF0E0F01881DDD88C6B250BAE92CF487BE3FBE02FD830CE200","maintainer":"MAXL-SPB","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"109.75.40.105","ipv6":"2001:470:70d6::1","port":33445,"tcp_ports":[],"public_key":"2B9CD794424FD579044EC2FC5252B23DF8B4AAF239C25074F70B1090C3F8C83A","maintainer":"nek","location":"AM","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"185.120.34.64","ipv6":"2a06:8ec0:1:bb::3862","port":33445,"tcp_ports":[],"public_key":"728925473812C7AAC482BE7250BCCAD0B8CB9F737BF3D42ABD34459C1768F854","maintainer":"Kostik","location":"GB","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0}]} diff --git a/src/bootstrap.c b/src/bootstrap.c index adb4e76..0d45e33 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -1,7 +1,7 @@ /* bootstrap.c * * - * Copyright (C) 2014 Toxic All Rights Reserved. + * Copyright (C) 2016 Toxic All Rights Reserved. * * This file is part of Toxic. * @@ -37,11 +37,23 @@ extern struct arg_opts arg_opts; /* Time to wait between bootstrap attempts */ #define TRY_BOOTSTRAP_INTERVAL 5 +#define IPv4_MAX_SIZE 64 +#define PORT_MAX_SIZE 5 -#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */ -#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */ +#define IPV4_JSON_VALUE "\"ipv4\":\"" +#define IPV4_JSON_VALUE_LEN (sizeof(IPV4_JSON_VALUE) - 1) + +#define PORT_JSON_VALUE "\"port\":" +#define PORT_JSON_VALUE_LEN (sizeof(PORT_JSON_VALUE) - 1) + +#define KEY_JSON_VALUE "\"public_key\":\"" +#define KEY_JSON_VALUE_LEN (sizeof(KEY_JSON_VALUE) - 1) + +#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */ +#define MAX_NODE_LINE 300 /* Max number of chars in a sever line (name + port + key) */ #define MAXNODES 50 #define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7) +#define MAX_NODELIST_SIZE (1024 * MAXNODES) static struct toxNodes { size_t lines; @@ -50,67 +62,104 @@ static struct toxNodes { char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; } toxNodes; -/* Load the DHT nodelist to memory. +/* Load the DHT nodelist to memory from json formatted nodes file obtained at https://nodes.tox.chat/json. * * Return 0 on success. * Return -1 if nodelist file cannot be opened. - * Return -2 if nodelist file does not contain any valid node entries. + * Return -2 if nodelist file cannot be parsed. + * Return -3 if nodelist file does not contain any valid node entries. */ int load_DHT_nodelist(void) { - const char *filename = !arg_opts.nodes_path[0] ? PACKAGE_DATADIR "/DHTnodes" : arg_opts.nodes_path; - FILE *fp = fopen(filename, "r"); if (fp == NULL) return -1; - char line[MAX_NODE_LINE]; + char line[MAX_NODELIST_SIZE]; - while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) { - size_t line_len = strlen(line); + if (fgets(line, sizeof(line), fp) == NULL) { + return -2; + } - if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) { - const char *name = strtok(line, " "); - const char *port_str = strtok(NULL, " "); - const char *key_ascii = strtok(NULL, " "); + const char *line_start = line; - if (name == NULL || port_str == NULL || key_ascii == NULL) - continue; + while ((line_start = strstr(line_start + 1, IPV4_JSON_VALUE)) && toxNodes.lines < MAXNODES) { + /* Extract IPv4 address */ + const char *ip_start = strstr(line_start, IPV4_JSON_VALUE); - long int port = strtol(port_str, NULL, 10); - - if (port <= 0 || port > MAX_PORT_RANGE) - continue; - - size_t key_len = strlen(key_ascii); - size_t name_len = strlen(name); - - if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN) - continue; - - snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name); - toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0; - toxNodes.ports[toxNodes.lines] = port; - - /* remove possible trailing newline from key string */ - char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1]; - memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2); - key_len = TOX_PUBLIC_KEY_SIZE * 2; - real_ascii_key[key_len] = '\0'; - - if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) - continue; - - toxNodes.lines++; + // TODO: These strlen() calls are very inefficient. Should probably keep track of length manually. + if (ip_start == NULL || strlen(ip_start) < IPV4_JSON_VALUE_LEN) { + continue; } + + ip_start += IPV4_JSON_VALUE_LEN; + int ip_len = char_find(0, ip_start, '"'); + + if (ip_len == 0 || ip_len > IPv4_MAX_SIZE) { + continue; + } + + char ipv4_string[ip_len + 1]; + memcpy(ipv4_string, ip_start, ip_len); + ipv4_string[ip_len] = 0; + + /* Extract port */ + const char *port_start = strstr(ip_start, PORT_JSON_VALUE); + + if (!port_start || strlen(port_start) < PORT_JSON_VALUE_LEN) { + continue; + } + + port_start += PORT_JSON_VALUE_LEN; + int port_len = char_find(0, port_start, ','); + + if (port_len == 0 || port_len > PORT_MAX_SIZE) { + continue; + } + + char port_string[port_len + 1]; + memcpy(port_string, port_start, port_len); + port_string[port_len] = 0; + + long int port = strtol(port_string, NULL, 10); + + if (port <= 0 || port > MAX_PORT_RANGE) + continue; + + /* Extract key */ + const char *key_start = strstr(port_start, KEY_JSON_VALUE); + + if (!key_start || strlen(key_start) < KEY_JSON_VALUE_LEN) { + continue; + } + + key_start += KEY_JSON_VALUE_LEN; + int key_len = char_find(0, key_start, '"'); + + if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { + continue; + } + + char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; + memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2); + key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; + + /* Add IP-Port-Key to nodes list */ + snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", ipv4_string); + toxNodes.ports[toxNodes.lines] = port; + + if (hex_string_to_bin(key_string, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) + continue; + + toxNodes.lines++; } fclose(fp); if (toxNodes.lines == 0) - return -2; + return -3; return 0; } diff --git a/src/bootstrap.h b/src/bootstrap.h index f442ce2..e3dfab5 100644 --- a/src/bootstrap.h +++ b/src/bootstrap.h @@ -1,7 +1,7 @@ /* bootstrap.h * * - * Copyright (C) 2014 Toxic All Rights Reserved. + * Copyright (C) 2016 Toxic All Rights Reserved. * * This file is part of Toxic. * @@ -23,10 +23,11 @@ /* Manages connection to the Tox DHT network. */ void do_tox_connection(Tox *m); -/* Load the DHT nodelist to memory. +/* Load the DHT nodelist to memory from json formatted nodes file obtained attempts https://nodes.tox.chat/json. * * Return 0 on success. * Return -1 if nodelist file cannot be opened. - * Return -2 if nodelist file does not contain any valid node entries. + * Return -2 if nodelist file cannot be parsed. + * Return -3 if nodelist file does not contain any valid node entries. */ int load_DHT_nodelist(void); diff --git a/src/misc_tools.c b/src/misc_tools.c index b737a7c..438e748 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -367,9 +367,13 @@ size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length) } /* returns index of the first instance of ch in s starting at idx. - returns length of s if char not found */ + returns length of s if char not found or 0 if s is NULL. */ int char_find(int idx, const char *s, char ch) { + if (!s) { + return 0; + } + int i = idx; for (i = idx; s[i]; ++i) { @@ -381,9 +385,13 @@ int char_find(int idx, const char *s, char ch) } /* returns index of the last instance of ch in s starting at len. - returns 0 if char not found (skips 0th index). */ + returns 0 if char not found or s is NULL (skips 0th index). */ int char_rfind(const char *s, char ch, int len) { + if (!s) { + return 0; + } + int i = 0; for (i = len; i > 0; --i) { diff --git a/src/misc_tools.h b/src/misc_tools.h index f4875b8..8d109a3 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -136,11 +136,11 @@ int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum); size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length); /* returns index of the first instance of ch in s starting at idx. - returns length of s if char not found */ + returns length of s if char not found or 0 if s is NULL. */ int char_find(int idx, const char *s, char ch); -/* returns index of the last instance of ch in s - returns 0 if char not found */ +/* returns index of the last instance of ch in s starting at len. + returns 0 if char not found or s is NULL (skips 0th index). */ int char_rfind(const char *s, char ch, int len); /* Converts bytes to appropriate unit and puts in buf as a string */ From 1f8c11a33a1b4988ffada0deacf9fcd9772f893f Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Sun, 18 Sep 2016 11:34:13 -0400 Subject: [PATCH 13/23] These strlen() calls are unncessary --- src/bootstrap.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/bootstrap.c b/src/bootstrap.c index 0d45e33..0e2e1ed 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -89,8 +89,7 @@ int load_DHT_nodelist(void) /* Extract IPv4 address */ const char *ip_start = strstr(line_start, IPV4_JSON_VALUE); - // TODO: These strlen() calls are very inefficient. Should probably keep track of length manually. - if (ip_start == NULL || strlen(ip_start) < IPV4_JSON_VALUE_LEN) { + if (ip_start == NULL) { continue; } @@ -108,7 +107,7 @@ int load_DHT_nodelist(void) /* Extract port */ const char *port_start = strstr(ip_start, PORT_JSON_VALUE); - if (!port_start || strlen(port_start) < PORT_JSON_VALUE_LEN) { + if (!port_start) { continue; } @@ -131,7 +130,7 @@ int load_DHT_nodelist(void) /* Extract key */ const char *key_start = strstr(port_start, KEY_JSON_VALUE); - if (!key_start || strlen(key_start) < KEY_JSON_VALUE_LEN) { + if (!key_start) { continue; } From a009f11c0c628ddf279819dedef2e75f0f0d0359 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Tue, 20 Sep 2016 00:00:09 -0400 Subject: [PATCH 14/23] Automatically update DHT nodeslist List is now automatically fetched from nodes.tox.chat and placed in the user config directory. The list is updated once every 30 days, and will attempt to detect problems and re-fetch the list if necessary. Also fixed a couple file descriptor leaks and cleaned some things up. --- Makefile | 8 +- cfg/global_vars.mk | 2 +- doc/toxic.1 | 12 +-- doc/toxic.1.asc | 8 +- misc/DHTnodes | 1 - src/bootstrap.c | 249 ++++++++++++++++++++++++++++++++++++++++----- src/bootstrap.h | 11 +- src/configdir.c | 8 +- src/configdir.h | 15 +++ src/curl_util.c | 93 +++++++++++++++++ src/curl_util.h | 50 +++++++++ src/name_lookup.c | 86 ++-------------- src/name_lookup.h | 3 +- src/toxic.c | 36 +++---- src/windows.h | 2 +- 15 files changed, 440 insertions(+), 144 deletions(-) delete mode 100644 misc/DHTnodes create mode 100644 src/curl_util.c create mode 100644 src/curl_util.h diff --git a/Makefile b/Makefile index abb09ff..9c55ae6 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,10 @@ CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' CFLAGS += $(USER_CFLAGS) LDFLAGS = $(USER_LDFLAGS) -OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o -OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o -OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o -OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o bootstrap.o +OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o +OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o +OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o +OBJ += term_mplex.o toxic.o toxic_strings.o windows.o # Check on wich system we are running UNAME_S = $(shell uname -s) diff --git a/cfg/global_vars.mk b/cfg/global_vars.mk index 2fa9723..0503b90 100644 --- a/cfg/global_vars.mk +++ b/cfg/global_vars.mk @@ -16,7 +16,7 @@ MISC_DIR = $(BASE_DIR)/misc # Project files MANFILES = toxic.1 toxic.conf.5 -DATAFILES = DHTnodes nameservers toxic.conf.example +DATAFILES = nameservers toxic.conf.example DESKFILE = toxic.desktop SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav diff --git a/doc/toxic.1 b/doc/toxic.1 index 9878cb1..a5c72eb 100644 --- a/doc/toxic.1 +++ b/doc/toxic.1 @@ -2,12 +2,12 @@ .\" Title: toxic .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 2015-12-07 +.\" Date: 2016-04-25 .\" Manual: Toxic Manual .\" Source: toxic __VERSION__ .\" Language: English .\" -.TH "TOXIC" "1" "2015\-12\-07" "toxic __VERSION__" "Toxic Manual" +.TH "TOXIC" "1" "2016\-04\-25" "toxic __VERSION__" "Toxic Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,8 +82,7 @@ Show help message .RS 4 Use specified \fInodes\-file\fR -for DHT bootstrap nodes, instead of -\fI__DATADIR__/DHTnodes\fR +for DHT bootstrap nodes, instead of the default .RE .PP \-o, \-\-noconnect @@ -122,9 +121,10 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat .RE .SH "FILES" .PP -__DATADIR__/DHTnodes +~/\&.config/tox/DHTnodes .RS 4 -Default list of DHT bootstrap nodes\&. +Default location for list of DHT bootstrap nodes (list obtained at +https://nodes\&.tox\&.chat)\&. This list is automatically updated every 30 days\&. .RE .PP ~/\&.config/tox/toxic_profile\&.tox diff --git a/doc/toxic.1.asc b/doc/toxic.1.asc index 20d3c00..b97af86 100644 --- a/doc/toxic.1.asc +++ b/doc/toxic.1.asc @@ -41,8 +41,7 @@ OPTIONS Show help message -n, --nodes nodes-file:: - Use specified 'nodes-file' for DHT bootstrap nodes, instead of - '{datadir}/DHTnodes' + Use specified 'nodes-file' for DHT bootstrap nodes, instead of the default -o, --noconnect:: Do not connect to the DHT network @@ -68,8 +67,9 @@ OPTIONS FILES ----- -{datadir}/DHTnodes:: - Default list of DHT bootstrap nodes. +~/.config/tox/DHTnodes:: + Default location for list of DHT bootstrap nodes (list obtained at https://nodes.tox.chat). + This list is automatically updated every 30 days. ~/.config/tox/toxic_profile.tox:: Savestate which contains your personal info (nickname, Tox ID, contacts, diff --git a/misc/DHTnodes b/misc/DHTnodes deleted file mode 100644 index bdaefc6..0000000 --- a/misc/DHTnodes +++ /dev/null @@ -1 +0,0 @@ -{"last_scan":1474137109,"nodes":[{"ipv4":"tox.zodiaclabs.org","ipv6":"v6.tox.zodiaclabs.org","port":33445,"tcp_ports":[33445,3389],"public_key":"A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074","maintainer":"stal","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"I am the bone of my code;\nNIH is my body and str8c is my blood.\nI have overflowed a thousand buffers.\nUnknown to SIGSEGV, nor to abort();\nHave withstood pain to create fast programs;\nYet my code will always be unsafe.\nSo as I pray, rock solid straight C.","last_ping":1474137098},{"ipv4":"biribiri.org","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67","maintainer":"nurupo","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Welcome, stranger #3783. I'm up for 1d 14h 23m 12s, running since Sep 16 04:08:25 UTC. If I get outdated, please ping my maintainer at nurupo.contributions@gmail.com","last_ping":1474137098},{"ipv4":"178.62.250.138","ipv6":"2a03:b0c0:2:d0::16:1","port":33445,"tcp_ports":[3389,33445],"public_key":"788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B","maintainer":"Impyy","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Impy's kickin' bootstrap node","last_ping":1474137097},{"ipv4":"130.133.110.14","ipv6":"2001:6f8:1c3c:babe::14:1","port":33445,"tcp_ports":[33445],"public_key":"461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F","maintainer":"Manolis","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Spline tox bootstrap node","last_ping":1474137101},{"ipv4":"205.185.116.116","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"198.98.51.198","ipv6":"2605:6400:1:fed5:22:45af:ec10:f329","port":33445,"tcp_ports":[33445,3389,443],"public_key":"1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F","maintainer":"Busindre","location":"US","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"139.162.217.110","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"0EEBE6304F4B3F6549F39A87FBB45751929F4833BA6AC5F35B6DFA79D01B4523","maintainer":"Kr9r0x","location":"GB","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137097},{"ipv4":"194.249.212.109","ipv6":"2001:1470:fbfe::109","port":33445,"tcp_ports":[3389,443,33445],"public_key":"3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B","maintainer":"fluke571","location":"SI","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"185.25.116.107","ipv6":"2a00:7a60:0:746b::3","port":33445,"tcp_ports":[33445,3389],"public_key":"DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43","maintainer":"MAH69K","location":"UA","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137102},{"ipv4":"192.99.168.140","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F","maintainer":"WIeschie","location":"CA","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"95.215.46.114","ipv6":"2a02:7aa0:1619::bdbd:17b8","port":33445,"tcp_ports":[33445,3389],"public_key":"5823FB947FF24CF83DDFAC3F3BAA18F96EA2018B16CC08429CB97FA502F40C23","maintainer":"Rotkaermota","location":"SE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137102},{"ipv4":"5.189.176.217","ipv6":"2a02:c200:1:10:3:1:605:1337","port":5190,"tcp_ports":[33445,5190,3389],"public_key":"2B2137E094F743AC8BD44652C55F41DFACC502F125E99E4FE24D40537489E32F","maintainer":"tastytea","location":"DE","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137101},{"ipv4":"148.251.23.146","ipv6":"2a01:4f8:201:8493::2","port":2306,"tcp_ports":[33445,2306],"public_key":"7AED21F94D82B05774F697B209628CD5A9AD17E0C073D9329076A4C28ED28147","maintainer":"pucetox","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"by pucetox,\n sync your nodes here tox.0x10k.com/bootstrapd-conf , \n for communication: 1D1C0B992DEB6D7F18561176F7F5E572BCC7F2BA5CFA7E9E437B9134122CE96D906A6119F9D2","last_ping":1474137097},{"ipv4":"104.223.122.15","ipv6":"2607:ff48:aa81:800::35eb:1","port":33445,"tcp_ports":[3389,33445],"public_key":"0FB96EEBFB1650DDB52E70CF773DDFCABE25A95CC3BB50FC251082E4B63EF82A","maintainer":"ru_maniac","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox id for queries and general info: EBD2A7B649ABB10ED9F47E5113F04000F39D46F087CEB62FCCE1069471FD6915256D197F2A97","last_ping":1474137098},{"ipv4":"tox.verdict.gg","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"1C5293AEF2114717547B39DA8EA6F1E331E5E358B35F9B6B5F19317911C5F976","maintainer":"Deliran","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Since 2015","last_ping":1474137098},{"ipv4":"d4rk4.ru","ipv6":"-","port":1813,"tcp_ports":[1813],"public_key":"53737F6D47FA6BD2808F378E339AF45BF86F39B64E79D6D491C53A1D522E7039","maintainer":"D4rk4","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"TOX ID: 35EDC07AEB18B163E07EE33F6CDDA63969F394FF6A617CEAB22A7EBBEAAAF854C0EDFBD46898","last_ping":1474137102},{"ipv4":"81.4.110.149","ipv6":"2a00:d880:3:2::8bdc:f19","port":33445,"tcp_ports":[33445,3389],"public_key":"9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E","maintainer":"tibietigni","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Tibietigni.com node. Tox_id for communication: D36CC0B702621F48FDBC540A57124A744E5133C932E65ACCEBCABF2586A02455171717175989","last_ping":1474137097},{"ipv4":"95.31.20.151","ipv6":"-","port":33445,"tcp_ports":[3389],"public_key":"9CA69BB74DE7C056D1CC6B16AB8A0A38725C0349D187D8996766958584D39340","maintainer":"IgorNovgorodov","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"happy-new-year","last_ping":1474137101},{"ipv4":"51.254.84.212","ipv6":"2001:41d0:a:1a3b::18","port":33445,"tcp_ports":[3389,33445],"public_key":"AEC204B9A4501412D5F0BB67D9C81B5DB3EE6ADA64122D32A3E9B093D544327D","maintainer":"a68366","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Since 26.12.2015","last_ping":1474137097},{"ipv4":"5.135.59.163","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"2D320F971EF2CA18004416C2AAE7BA52BF7949DB34EA8E2E21AF67BD367BE211","maintainer":"Skey","location":"FR","status_udp":true,"status_tcp":true,"version":"2014101200","motd":"tox-bootstrapd","last_ping":1474137097},{"ipv4":"185.58.206.164","ipv6":"2a02:f680:1:1100::3313","port":33445,"tcp_ports":[3389,33445],"public_key":"24156472041E5F220D1FA11D9DF32F7AD697D59845701CDD7BE7D1785EB9DB39","maintainer":"ru_maniac","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"please note: running on iphy's fork!\nmore info on the matter: goo.gl/Gz5KhK \u0026 goo.gl/i2TZJr\n\ntox id for queries and general info: EBD2A7B649ABB10ED9F47E5113F04000F39D46F087CEB62FCCE1069471FD6915256D197F2A97","last_ping":1474137098},{"ipv4":"128.199.199.197","ipv6":"2400:6180:0:d0::17a:a001","port":33445,"tcp_ports":[3389,33445],"public_key":"B05C8869DBB4EDDD308F43C1A974A20A725A36EACCA123862FDE9945BF9D3E09","maintainer":"wiiaam","location":"SG","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"apt-get install antox","last_ping":1474137098},{"ipv4":"bootybay.club","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"0CB9D8D636F8E3D71CF44A3019408A846B7BEDFA2810853355DB58C0A36BFE38","maintainer":"AssPirate","location":"AU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"91.121.66.124","ipv6":"2001:41d0:1:757c::1","port":33445,"tcp_ports":[33445],"public_key":"4E3F7D37295664BBD0741B6DBCB6431D6CD77FC4105338C2FC31567BF5C8224A","maintainer":"h4nt3r","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox id for queries and general info: 3BC3C6875508A2EC86BE3E35E4FA9155E444CFA96671AE7D7D0D2585A0A5FA38E071A5E463E5","last_ping":1474137097},{"ipv4":"195.93.190.6","ipv6":"2a01:d0:ffff:a8a::2","port":33445,"tcp_ports":[33445],"public_key":"FB4CE0DDEFEED45F26917053E5D24BDDA0FA0A3D83A672A9DA2375928B37023D","maintainer":"strngr","location":"UA","status_udp":true,"status_tcp":true,"version":"","motd":"","last_ping":1474137105},{"ipv4":"loki.uplinklabs.net","ipv6":"loki.uplinklabs.net","port":33445,"tcp_ports":[3389,33445],"public_key":"1A56EA3EDF5DF4C0AEABBF3C2E4E603890F87E983CAC8A0D532A335F2C6E3E1F","maintainer":"AbacusAvenger","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"95.215.44.78","ipv6":"2a02:7aa0:1619::c6fe:d0cb","port":33445,"tcp_ports":[33445,3389],"public_key":"672DBE27B4ADB9D5FB105A6BB648B2F8FDB89B3323486A7A21968316E012023C","maintainer":"HooinKyoma","location":"SE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Thanx to Hooin Kyoma","last_ping":1474137098},{"ipv4":"tox.ntp.moscow","ipv6":"tox.ntp.moscow","port":33445,"tcp_ports":[33445,3389],"public_key":"80EF8660D9C5ACE1577BAB031375A0F08284CBFD9F810A857955DCC87377FC4D","maintainer":"abbat","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-easy-bootstrap","last_ping":1474137098},{"ipv4":"146.185.136.123","ipv6":"2a03:b0c0:0:1010::389:1","port":33445,"tcp_ports":[33445,443,3389],"public_key":"09993FAF174DFFDC515B398A2EFC5639C4E6D7B736FC864F89786B50EAF88C1A","maintainer":"LittleVulpix","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"LittleTox - your friendly neighbourhood tox node from the land of windmills!","last_ping":1474137097},{"ipv4":"163.172.136.118","ipv6":"2001:bc8:4400:2100::1c:50f","port":33445,"tcp_ports":[33445,3389],"public_key":"2C289F9F37C20D09DA83565588BF496FAB3764853FA38141817A72E3F18ACA0B","maintainer":"LittleVulpix","location":"FR","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"LittleTox - your friendly neighbourhood tox node!","last_ping":1474137097},{"ipv4":"37.97.185.116","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"E59A0E71ADA20D35BD1B0957059D7EF7E7792B3D680AE25C6F4DBBA09114D165","maintainer":"Yani","location":"NL","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Yani's node of pleasure and leisure","last_ping":1474137097},{"ipv4":"193.124.186.205","ipv6":"2a02:f680:1:1100::542a","port":5228,"tcp_ports":[443,3389,33445],"public_key":"9906D65F2A4751068A59D30505C5FC8AE1A95E0843AE9372EAFA3BAB6AC16C2C","maintainer":"Cactus","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137098},{"ipv4":"80.87.193.193","ipv6":"2a01:230:2:6::46a8","port":33445,"tcp_ports":[33445,3389],"public_key":"B38255EE4B054924F6D79A5E6E5889EC94B6ADF6FE9906F97A3D01E3D083223A","maintainer":"linxon","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Free Linxon's TOX Bootstrap node","last_ping":1474137098},{"ipv4":"initramfs.io","ipv6":"-","port":33445,"tcp_ports":[33445,3389],"public_key":"3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25","maintainer":"initramfs","location":"TW","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"initramfs' Tox DHT Node","last_ping":1474137099},{"ipv4":"tox.neverlocate.me","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"49183DBF0E865713154069D1C7C7A2732ED78CF32C4D76AF5304FE31C5FEB81A","maintainer":"brandon","location":"US","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137102},{"ipv4":"shigure.eve.moe","ipv6":"shigure.eve.moe","port":33445,"tcp_ports":[3389,33445],"public_key":"1A480A53FAF2CBBFCC382D786C43E69EEE23F22C7C23A7CFEB6180A373E23E54","maintainer":"EveNeko","location":"GB","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"tox-bootstrapd@shigure.eve.moe","last_ping":1474137098},{"ipv4":"tox.deadteam.org","ipv6":"tox.deadteam.org","port":33445,"tcp_ports":[33445],"public_key":"C7D284129E83877D63591F14B3F658D77FF9BA9BA7293AEB2BDFBFE1A803AF47","maintainer":"DeadTeam","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Vive le TOX","last_ping":1474137101},{"ipv4":"prok.pw","ipv6":"-","port":33445,"tcp_ports":[3389,33445],"public_key":"69C3FEBB977687B64FA0213BDEB89A43463BB48DED288150CFFB6429EFF82436","maintainer":"Prototik","location":"RU","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Have a nice chatting!","last_ping":1474137098},{"ipv4":"maggie.prok.pw","ipv6":"maggie.prok.pw","port":33445,"tcp_ports":[33445,3389],"public_key":"B75583B6D967DB8AD7C6D3B6F9318194BCC79B2FEF18F69E2DF275B779E7AA30","maintainer":"Prototik","location":"DE","status_udp":true,"status_tcp":true,"version":"2016010100","motd":"Maggie wants a pacifier","last_ping":1474137101},{"ipv4":"108.61.165.198","ipv6":"2001:19f0:5000:8121:5054:ff:fe1c:9ded","port":33445,"tcp_ports":[],"public_key":"8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832","maintainer":"ray65536","location":"NL","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"Ray's Tox Node","last_ping":1474137101},{"ipv4":"92.54.84.70","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5625A62618CB4FCA70E147A71B29695F38CC65FF0CBD68AD46254585BE564802","maintainer":"t3mp","location":"RU","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"tox-bootstrapd","last_ping":1474137102},{"ipv4":"srv1.ricin.im","ipv6":"-","port":1337,"tcp_ports":[],"public_key":"3651DAB570D7F60381F87B19D6935EE7F5FE01308DCE71C4B69993150C6A903C","maintainer":"Ricin.im","location":"FR","status_udp":true,"status_tcp":false,"version":"2016010100","motd":"Ricin.im Tox node. Enjoy babes \u003c3","last_ping":1474137101},{"ipv4":"46.101.197.175","ipv6":"2a03:b0c0:3:d0::ac:5001","port":443,"tcp_ports":[443,33445,3389],"public_key":"CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707","maintainer":"clearmartin","location":"DE","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137105},{"ipv4":"toxnode.nek0.net","ipv6":"toxnode.nek0.net","port":33445,"tcp_ports":[3389,33445],"public_key":"20965721D32CE50C3E837DD75B33908B33037E6225110BFF209277AEAF3F9639","maintainer":"Phsm","location":"UA","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137107},{"ipv4":"sorunome.de","ipv6":"sorunome.de","port":33445,"tcp_ports":[33445,3389],"public_key":"02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46","maintainer":"Sorunome","location":"DE","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137105},{"ipv4":"hibiki.eve.moe","ipv6":"hibiki.eve.moe","port":33445,"tcp_ports":[33445],"public_key":"D3EB45181B343C2C222A5BCF72B760638E15ED87904625AAD351C594EEFAE03E","maintainer":"EveNeko","location":"US","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137098},{"ipv4":"46.229.52.198","ipv6":"-","port":33445,"tcp_ports":[33445],"public_key":"813C8F4187833EF0655B10F7752141A352248462A567529A38B6BBF73E979307","maintainer":"Stranger","location":"UA","status_udp":false,"status_tcp":true,"version":"","motd":"","last_ping":1474137109},{"ipv4":"144.76.60.215","ipv6":"2a01:4f8:191:64d6::1","port":33445,"tcp_ports":[],"public_key":"04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F","maintainer":"sonOfRa","location":"DE","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"195.154.119.113","ipv6":"2001:bc8:3698:101::1","port":33445,"tcp_ports":[],"public_key":"E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354","maintainer":"Munrek","location":"FR","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"46.38.239.179","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A","maintainer":"MartinSchröder","location":"DE","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"104.167.101.29","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57","maintainer":"noisykeyboard","location":"CA","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"80.232.246.79","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"CF6CECA0A14A31717CC8501DA51BE27742B70746956E6676FF423A529F91ED5D","maintainer":"fUNKIAM","location":"LV","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"104.233.104.126","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"EDEE8F2E839A57820DE3DA4156D88350E53D4161447068A3457EE8F59F362414","maintainer":"wildermesser","location":"CA","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"home.vikingmakt.com.br","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"188E072676404ED833A4E947DC1D223DF8EFEBE5F5258573A236573688FB9761","maintainer":"umgeher","location":"BR","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"188.244.38.183","ipv6":"2001:470:de00:2:20c:29ff:fe68:354f","port":33445,"tcp_ports":[],"public_key":"15A0F9684E2423F9F46CFA5A50B562AE42525580D840CC50E518192BF333EE38","maintainer":"gem","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"mrflibble.c4.ee","ipv6":"2a02:16e0:0:12::4","port":33445,"tcp_ports":[],"public_key":"FAAB17014F42F7F20949F61E55F66A73C230876812A9737F5F6D2DCE4D9E4207","maintainer":"mrflibble","location":"GB","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"82.211.31.116","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"AF97B76392A6474AF2FD269220FDCF4127D86A42EF3A242DF53A40A268A2CD7C","maintainer":"Net.Verified","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"tox1.privacydragon.me","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"31910C0497D347FF160D6F3A6C0E317BAFA71E8E03BC4CBB2A185C9D4FB8B31E","maintainer":"PrivacyDragon","location":"US","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"zawertun.net","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"5521952892FBD5C185DF7180DB4DEF69D7844DEEE79B1F75A634ED9DF656756E","maintainer":"ZaWertun","location":"NL","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"87.98.168.93","ipv6":"-","port":33445,"tcp_ports":[],"public_key":"C3F6C06A624FAE086DA94604A7838DB495769807EC055FADA36EBF2D4484FB33","maintainer":"_kinka_","location":"ES","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"185.61.253.189","ipv6":"2a04:ee00:0:9:20c:29ff:fe27:ad96","port":33445,"tcp_ports":[],"public_key":"73EEBCB4CBBE56BF0E0F01881DDD88C6B250BAE92CF487BE3FBE02FD830CE200","maintainer":"MAXL-SPB","location":"RU","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"109.75.40.105","ipv6":"2001:470:70d6::1","port":33445,"tcp_ports":[],"public_key":"2B9CD794424FD579044EC2FC5252B23DF8B4AAF239C25074F70B1090C3F8C83A","maintainer":"nek","location":"AM","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0},{"ipv4":"185.120.34.64","ipv6":"2a06:8ec0:1:bb::3862","port":33445,"tcp_ports":[],"public_key":"728925473812C7AAC482BE7250BCCAD0B8CB9F737BF3D42ABD34459C1768F854","maintainer":"Kostik","location":"GB","status_udp":false,"status_tcp":false,"version":"","motd":"","last_ping":0}]} diff --git a/src/bootstrap.c b/src/bootstrap.c index 0e2e1ed..244ecc1 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -23,23 +23,40 @@ #include #include #include +#include +#include #include #include "line_info.h" #include "windows.h" #include "misc_tools.h" +#include "configdir.h" +#include "curl_util.h" extern struct arg_opts arg_opts; +/* URL that we get the JSON encoded nodes list from. */ +#define NODES_LIST_URL "https://nodes.tox.chat/json" + +#define DEFAULT_NODES_FILENAME "DHTnodes" /* Time to wait between bootstrap attempts */ #define TRY_BOOTSTRAP_INTERVAL 5 +/* Number of nodes to bootstrap to per try */ +#define NUM_BOOTSTRAP_NODES 5 + +/* How often we should update the nodeslist file. */ +#define NODELIST_UPDATE_TIMEOUT (60*24*30) + #define IPv4_MAX_SIZE 64 #define PORT_MAX_SIZE 5 +#define LAST_SCAN_JSON_VALUE "\"last_scan\":" +#define LAST_SCAN_JSON_VALUE_LEN (sizeof(LAST_SCAN_JSON_VALUE) - 1) + #define IPV4_JSON_VALUE "\"ipv4\":\"" #define IPV4_JSON_VALUE_LEN (sizeof(IPV4_JSON_VALUE) - 1) @@ -62,24 +79,198 @@ static struct toxNodes { char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; } toxNodes; -/* Load the DHT nodelist to memory from json formatted nodes file obtained at https://nodes.tox.chat/json. + +/* Return true if nodeslist pointed to by fp needs to be updated. + * This will be the case if the file is empty, has an invalid format, + * or if the file is older than the given timeout. + */ +static bool nodeslist_needs_update(const char *nodes_path) +{ + FILE *fp = fopen(nodes_path, "r+"); + + if (fp == NULL) { + return false; + } + + /* last_scan value should be at beginning of file */ + char line[LAST_SCAN_JSON_VALUE_LEN + 32]; + + if (fgets(line, sizeof(line), fp) == NULL) { + fclose(fp); + return true; + } + + fclose(fp); + + const char *last_scan_val = strstr(line, LAST_SCAN_JSON_VALUE); + + if (last_scan_val == NULL) { + return true; + } + + last_scan_val += LAST_SCAN_JSON_VALUE_LEN; + long long int last_scan = strtoll(last_scan_val, NULL, 10); + + if (timed_out(last_scan, NODELIST_UPDATE_TIMEOUT)) { + return true; + } + + return false; +} + +/* Fetches the JSON encoded DHT nodeslist from NODES_LIST_URL. * * Return 0 on success. - * Return -1 if nodelist file cannot be opened. - * Return -2 if nodelist file cannot be parsed. - * Return -3 if nodelist file does not contain any valid node entries. + * Return -1 on failure. */ -int load_DHT_nodelist(void) +static int curl_fetch_nodes_JSON(struct Recv_Data *recv_data) { - const char *filename = !arg_opts.nodes_path[0] ? PACKAGE_DATADIR "/DHTnodes" : arg_opts.nodes_path; - FILE *fp = fopen(filename, "r"); + CURL *c_handle = curl_easy_init(); - if (fp == NULL) + if (c_handle == NULL) { return -1; + } + + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, "charsets: utf-8"); + + curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL); + curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, write_lookup_data); + curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data); + curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L); + + int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); + + if (proxy_ret != 0) { + fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret); + return -1; + } + + int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + + if (ret != CURLE_OK) { + fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret); + return -1; + } + + ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST); + + if (ret != CURLE_OK) { + fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret); + return -1; + } + + ret = curl_easy_perform(c_handle); + + if (ret != CURLE_OK) { + /* If system doesn't support any of the specified ciphers suites, fall back to default */ + if (ret == CURLE_SSL_CIPHER) { + curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL); + ret = curl_easy_perform(c_handle); + } + + if (ret != CURLE_OK) { + fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret); + return -1; + } + } + + return 0; +} + +/* Attempts to update the DHT nodeslist. + * + * Return 1 if list was updated successfully. + * Return 0 if list does not need to be updated. + * Return -1 if file cannot be opened. + * Return -2 if http lookup failed. + * Return -3 if http reponse was empty. + * Return -4 if data could not be written to disk. + */ +static int update_DHT_nodeslist(const char *nodes_path) +{ + if (!nodeslist_needs_update(nodes_path)) { + return 0; + } + + FILE *fp = fopen(nodes_path, "r+"); + + if (fp == NULL) { + return -1; + } + + struct Recv_Data recv_data; + memset(&recv_data, 0, sizeof(struct Recv_Data)); + + if (curl_fetch_nodes_JSON(&recv_data) == -1) { + fclose(fp); + return -2; + } + + if (recv_data.length == 0) { + fclose(fp); + return -3; + } + + if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) { + fclose(fp); + return -4; + } + + fclose(fp); + return 1; +} + +static void get_nodeslist_path(char *buf, size_t buf_size) +{ + char *config_dir = NULL; + + if (arg_opts.nodes_path[0]) { + snprintf(buf, buf_size, "%s", arg_opts.nodes_path); + } else if ((config_dir = get_user_config_dir()) != NULL) { + snprintf(buf, buf_size, "%s%s%s", config_dir, CONFIGDIR, DEFAULT_NODES_FILENAME); + free(config_dir); + } else { + snprintf(buf, buf_size, "%s", DEFAULT_NODES_FILENAME); + } +} + +/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. + * TODO: Parse json using a proper library? + * + * Return 0 on success. + * Return -1 if nodeslist file cannot be opened or created. + * Return -2 if nodeslist file cannot be parsed. + * Return -3 if nodeslist file does not contain any valid node entries. + */ +int load_DHT_nodeslist(void) +{ + char nodes_path[PATH_MAX]; + get_nodeslist_path(nodes_path, sizeof(nodes_path)); + + FILE *fp = NULL; + + if (!file_exists(nodes_path)) { + if ((fp = fopen(nodes_path, "w+")) == NULL) { + return -1; + } + } else if ((fp = fopen(nodes_path, "r+")) == NULL) { + return -1; + } + + int update_err = update_DHT_nodeslist(nodes_path); + + if (update_err < 0) { + fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err); + } char line[MAX_NODELIST_SIZE]; if (fgets(line, sizeof(line), fp) == NULL) { + fclose(fp); return -2; } @@ -124,8 +315,9 @@ int load_DHT_nodelist(void) long int port = strtol(port_string, NULL, 10); - if (port <= 0 || port > MAX_PORT_RANGE) + if (port <= 0 || port > MAX_PORT_RANGE) { continue; + } /* Extract key */ const char *key_start = strstr(port_start, KEY_JSON_VALUE); @@ -145,7 +337,7 @@ int load_DHT_nodelist(void) memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2); key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; - /* Add IP-Port-Key to nodes list */ + /* Add entry to nodes list */ snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", ipv4_string); toxNodes.ports[toxNodes.lines] = port; @@ -155,34 +347,45 @@ int load_DHT_nodelist(void) toxNodes.lines++; } - fclose(fp); - - if (toxNodes.lines == 0) + /* If nodeslist does not contain any valid entries we set the last_scan value + * to 0 so that it will fetch a new list the next time this function is called. + */ + if (toxNodes.lines == 0) { + const char *s = "{\"last_scan\":0}"; + rewind(fp); + fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails + fclose(fp); return -3; + } + fclose(fp); return 0; } -/* Connects to a random DHT node listed in the DHTnodes file. */ +/* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ static void DHT_bootstrap(Tox *m) { if (toxNodes.lines == 0) { return; } - size_t node = rand() % toxNodes.lines; + size_t i; - TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { + size_t node = rand() % toxNodes.lines; - if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); - } + TOX_ERR_BOOTSTRAP err; + tox_bootstrap(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); - tox_add_tcp_relay(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + if (err != TOX_ERR_BOOTSTRAP_OK) { + fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + } - if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + tox_add_tcp_relay(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + + if (err != TOX_ERR_BOOTSTRAP_OK) { + fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + } } } diff --git a/src/bootstrap.h b/src/bootstrap.h index e3dfab5..0159d3d 100644 --- a/src/bootstrap.h +++ b/src/bootstrap.h @@ -23,11 +23,12 @@ /* Manages connection to the Tox DHT network. */ void do_tox_connection(Tox *m); -/* Load the DHT nodelist to memory from json formatted nodes file obtained attempts https://nodes.tox.chat/json. +/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. + * TODO: Parse json using a proper library? * * Return 0 on success. - * Return -1 if nodelist file cannot be opened. - * Return -2 if nodelist file cannot be parsed. - * Return -3 if nodelist file does not contain any valid node entries. + * Return -1 if nodeslist file cannot be opened or created. + * Return -2 if nodeslist file cannot be parsed. + * Return -3 if nodeslist file does not contain any valid node entries. */ -int load_DHT_nodelist(void); +int load_DHT_nodeslist(void); diff --git a/src/configdir.c b/src/configdir.c index d1e5f8d..ee97af3 100644 --- a/src/configdir.c +++ b/src/configdir.c @@ -33,7 +33,7 @@ #include "configdir.h" #include "misc_tools.h" -/* get the user's home directory */ +/* get the user's home directory. */ void get_home_dir(char *home, int size) { struct passwd pwd; @@ -102,8 +102,10 @@ char *get_user_config_dir(void) return user_config_dir; } -/* - * Creates the config and chatlog directories. +/* Creates the config and chatlog directories. + * + * Returns 0 on success. + * Returns -1 on failure. */ int create_user_config_dirs(char *path) { diff --git a/src/configdir.h b/src/configdir.h index 69abffd..a5317c3 100644 --- a/src/configdir.h +++ b/src/configdir.h @@ -34,8 +34,23 @@ #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif +/** + * @brief Get the user's config directory. + * + * This is without a trailing slash. Resulting string must be freed. + * + * @return The users config dir or NULL on error. + */ char *get_user_config_dir(void); + +/* get the user's home directory. */ void get_home_dir(char *home, int size); + +/* Creates the config and chatlog directories. + * + * Returns 0 on success. + * Returns -1 on failure. + */ int create_user_config_dirs(char *path); #endif /* #define CONFIGDIR_H */ diff --git a/src/curl_util.c b/src/curl_util.c new file mode 100644 index 0000000..5ab2944 --- /dev/null +++ b/src/curl_util.c @@ -0,0 +1,93 @@ +/* curl_util.c + * + * + * Copyright (C) 2016 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +#include +#include + +#include +#include + +#include "curl_util.h" + +/* Sets proxy info for given CURL handler. + * + * Returns 0 on success or if no proxy is set by the client. + * Returns -1 if proxy info is invalid. + * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html) + */ +int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type) +{ + if (proxy_type == TOX_PROXY_TYPE_NONE) + return 0; + + if (proxy_address == NULL || port == 0) { + return -1; + } + + int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port); + + if (ret != CURLE_OK) { + return ret; + } + + long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP; + + ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type); + + if (ret != CURLE_OK) { + return ret; + } + + ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address); + + if (ret != CURLE_OK) { + return ret; + } + + return 0; +} + +/* Callback function for CURL to write received data. + * + * This function will append data from an http request to the data buffer + * until the request is complete or the buffer is full. Buffer will be null terminated. + * + * Returns number of bytes received from http request on success (don't change this). + * Returns 0 if data exceeds buffer size. + */ +size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer) +{ + struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer; + + size_t length = size * nmemb; + size_t total_size = length + recv_data->length; + + if (total_size > MAX_RECV_CURL_DATA_SIZE) { + return 0; + } + + memcpy(recv_data->data + recv_data->length, data, length); + recv_data->data[total_size] = '\0'; + recv_data->length += length; + + return length; +} diff --git a/src/curl_util.h b/src/curl_util.h new file mode 100644 index 0000000..b6b67f1 --- /dev/null +++ b/src/curl_util.h @@ -0,0 +1,50 @@ +/* curl_util.h + * + * + * Copyright (C) 2016 Toxic All Rights Reserved. + * + * This file is part of Toxic. + * + * Toxic is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Toxic is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Toxic. If not, see . + * + */ + +/* List based on Mozilla's recommended configurations for modern browsers */ +#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" + +/* Max size of an http response that we can store in Recv_Data */ +#define MAX_RECV_CURL_DATA_SIZE 32767 + +/* Holds data received from curl lookup */ +struct Recv_Data { + char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */ + size_t length; /* Total number of bytes written to data buffer (doesn't include null) */ +}; + +/* Sets proxy info for given CURL handler. + * + * Returns 0 on success or if no proxy is set by the client. + * Returns -1 if proxy info is invalid. + * Returns an int > 0 on curl error (see: https://curl.haxx.se/libcurl/c/libcurl-errors.html) + */ +int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type); + +/* Callback function for CURL to write received data. + * + * This function will append data from an http request to the data buffer + * until the request is complete or the buffer is full. Buffer will be null terminated. + * + * Returns size of bytes written to the data buffer. + */ +size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer); diff --git a/src/name_lookup.c b/src/name_lookup.c index 3fc7e36..863e9aa 100644 --- a/src/name_lookup.c +++ b/src/name_lookup.c @@ -22,7 +22,6 @@ #include #include -#include /* for u_char */ #include #include "toxic.h" @@ -31,6 +30,7 @@ #include "global_commands.h" #include "misc_tools.h" #include "configdir.h" +#include "curl_util.h" extern struct arg_opts arg_opts; extern struct Winthread Winthread;; @@ -41,9 +41,6 @@ extern struct Winthread Winthread;; #define MAX_DOMAIN_SIZE 32 #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 -/* List based on Mozilla's recommended configurations for modern browsers */ -#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" - struct Nameservers { int lines; char names[MAX_SERVERS][MAX_DOMAIN_SIZE]; @@ -189,29 +186,6 @@ static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_s return false; } -#define MAX_RECV_LOOKUP_DATA_SIZE 1024 - -/* Holds raw data received from name server */ -struct Recv_Data { - char data[MAX_RECV_LOOKUP_DATA_SIZE]; - size_t size; -}; - -size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer) -{ - struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer; - size_t real_size = size * nmemb; - - if (real_size >= MAX_RECV_LOOKUP_DATA_SIZE) - return 0; - - memcpy(&recv_data->data, data, real_size); - recv_data->size = real_size; - recv_data->data[real_size] = '\0'; - - return real_size; -} - /* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer. * * Returns 0 on success. @@ -222,7 +196,7 @@ static int process_response(struct Recv_Data *recv_data) { size_t prefix_size = strlen(ID_PREFIX); - if (recv_data->size < TOX_ADDRESS_SIZE * 2 + prefix_size) + if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) return -1; const char *IDstart = strstr(recv_data->data, ID_PREFIX); @@ -243,47 +217,6 @@ static int process_response(struct Recv_Data *recv_data) return 0; } -/* Sets proxy info for given CURL handler. - * - * Returns 0 on success or if no proxy is set by the client. - * Returns -1 on failure. - */ -static int set_lookup_proxy(ToxWindow *self, CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type) -{ - if (proxy_type == TOX_PROXY_TYPE_NONE) - return 0; - - if (proxy_address == NULL || port == 0) { - lookup_error(self, "Unknown proxy error"); - return -1; - } - - int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port); - - if (ret != CURLE_OK) { - lookup_error(self, "Failed to set proxy port (libcurl error %d)", ret); - return -1; - } - - long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP; - - ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type); - - if (ret != CURLE_OK) { - lookup_error(self, "Failed to set proxy type (libcurl error %d)", ret); - return -1; - } - - ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address); - - if (ret != CURLE_OK) { - lookup_error(self, "Failed to set proxy (libcurl error %d)", ret); - return -1; - } - - return 0; -} - void *lookup_thread_func(void *data) { ToxWindow *self = t_data.self; @@ -333,8 +266,12 @@ void *lookup_thread_func(void *data) curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data); - if (set_lookup_proxy(self, c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type) == -1) + int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); + + if (proxy_ret != 0) { + lookup_error(self, "Failed to set proxy (error %d)\n"); goto on_exit; + } int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL); @@ -437,9 +374,9 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, * Returns -2 if the nameserver list cannot be found. * Returns -3 if the nameserver list does not contain any valid entries. */ -int name_lookup_init(void) +int name_lookup_init(int curl_init_status) { - if (curl_global_init(CURL_GLOBAL_ALL) != 0) { + if (curl_init_status != 0) { t_data.disabled = true; return -1; } @@ -454,8 +391,3 @@ int name_lookup_init(void) return 0; } - -void name_lookup_cleanup(void) -{ - curl_global_cleanup(); -} diff --git a/src/name_lookup.h b/src/name_lookup.h index c1419d8..e9b8ed4 100644 --- a/src/name_lookup.h +++ b/src/name_lookup.h @@ -29,8 +29,7 @@ * Returns 0 on success. * Returns -1 on failure. */ -int name_lookup_init(void); -void name_lookup_cleanup(void); +int name_lookup_init(int curl_init_status); int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message); diff --git a/src/toxic.c b/src/toxic.c index ab56f5c..2691656 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -164,7 +165,7 @@ void exit_toxic_success(Tox *m) free_global_data(); tox_kill(m); endwin(); - name_lookup_cleanup(); + curl_global_cleanup(); #ifdef X11 /* We have to terminate xtra last coz reasons @@ -455,6 +456,7 @@ int store_data(Tox *m, const char *path) char *data = malloc(data_len * sizeof(char)); if (data == NULL) { + fclose(fp); return -1; } @@ -465,6 +467,7 @@ int store_data(Tox *m, const char *path) char *enc_data = malloc(enc_len * sizeof(char)); if (enc_data == NULL) { + fclose(fp); free(data); return -1; } @@ -580,14 +583,14 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW if (len == 0) { fclose(fp); - exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); + exit_toxic_err("failed in load_tox", FATALERR_FILEOP); } char data[len]; if (fread(data, sizeof(data), 1, fp) != 1) { fclose(fp); - exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); + exit_toxic_err("failed in load_tox", FATALERR_FILEOP); } bool is_encrypted = tox_is_data_encrypted((uint8_t *) data); @@ -595,7 +598,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW /* attempt to encrypt an already encrypted data file */ if (arg_opts.encrypt_data && is_encrypted) { fclose(fp); - exit_toxic_err("failed in load_toxic", FATALERR_ENCRYPT); + exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT); } if (arg_opts.unencrypt_data && is_encrypted) @@ -681,7 +684,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW fclose(fp); } else { /* Data file does not/should not exist */ if (file_exists(data_path)) - exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); + exit_toxic_err("failed in load_tox", FATALERR_FILEOP); tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE; @@ -691,7 +694,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW return NULL; if (store_data(m, data_path) == -1) - exit_toxic_err("failed in load_toxic", FATALERR_FILEOP); + exit_toxic_err("failed in load_tox", FATALERR_FILEOP); } return m; @@ -920,10 +923,6 @@ static void parse_args(int argc, char *argv[]) case 'n': snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); - - if (!file_exists(arg_opts.nodes_path)) - queue_init_message("DHTnodes file not found"); - break; case 'o': @@ -1092,6 +1091,7 @@ void DnD_callback(const char* asdv, DropType dt) int main(int argc, char **argv) { + update_unix_time(); parse_args(argc, argv); /* Use the -b flag to enable stderr */ @@ -1125,16 +1125,12 @@ int main(int argc, char **argv) const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; - if (settings_load(user_settings, p) == -1) + if (settings_load(user_settings, p) == -1) { queue_init_message("Failed to load user settings"); - - int nodelist_ret = load_DHT_nodelist(); - - if (nodelist_ret != 0) { - queue_init_message("DHT nodelist failed to load (error %d). You can still connect manually with the /connect command.", nodelist_ret); } - int nameserver_ret = name_lookup_init(); + int curl_init = curl_global_init(CURL_GLOBAL_ALL); + int nameserver_ret = name_lookup_init(curl_init); if (nameserver_ret == -1) { queue_init_message("curl failed to initialize; name lookup service is disabled."); @@ -1144,6 +1140,12 @@ int main(int argc, char **argv) queue_init_message("Name lookup server list does not contain any valid entries."); } + int nodeslist_ret = load_DHT_nodeslist(); + + if (nodeslist_ret != 0) { + queue_init_message("DHT nodeslist failed to load (error %d)\n", nodeslist_ret); + } + #ifdef X11 if (init_xtra(DnD_callback) == -1) queue_init_message("X failed to initialize"); diff --git a/src/windows.h b/src/windows.h index c2a0760..21f1386 100644 --- a/src/windows.h +++ b/src/windows.h @@ -153,7 +153,7 @@ struct ToxWindow { #ifdef VIDEO int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */ - + #endif /* VIDEO */ #endif /* AUDIO */ From 4f6c6035436cbaf017634d5b587cc12955f0bb2c Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Tue, 20 Sep 2016 00:53:53 -0400 Subject: [PATCH 15/23] Rename DHTnodes to DHTnodes.json --- doc/toxic.1 | 8 ++++---- doc/toxic.1.asc | 4 ++-- src/bootstrap.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/toxic.1 b/doc/toxic.1 index a5c72eb..22101af 100644 --- a/doc/toxic.1 +++ b/doc/toxic.1 @@ -2,12 +2,12 @@ .\" Title: toxic .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 2016-04-25 +.\" Date: 2016-09-20 .\" Manual: Toxic Manual .\" Source: toxic __VERSION__ .\" Language: English .\" -.TH "TOXIC" "1" "2016\-04\-25" "toxic __VERSION__" "Toxic Manual" +.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -121,9 +121,9 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat .RE .SH "FILES" .PP -~/\&.config/tox/DHTnodes +~/\&.config/tox/DHTnodes\&.json .RS 4 -Default location for list of DHT bootstrap nodes (list obtained at +Default location for list of DHT bootstrap nodes (list obtained from https://nodes\&.tox\&.chat)\&. This list is automatically updated every 30 days\&. .RE .PP diff --git a/doc/toxic.1.asc b/doc/toxic.1.asc index b97af86..9abd941 100644 --- a/doc/toxic.1.asc +++ b/doc/toxic.1.asc @@ -67,8 +67,8 @@ OPTIONS FILES ----- -~/.config/tox/DHTnodes:: - Default location for list of DHT bootstrap nodes (list obtained at https://nodes.tox.chat). +~/.config/tox/DHTnodes.json:: + Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat). This list is automatically updated every 30 days. ~/.config/tox/toxic_profile.tox:: diff --git a/src/bootstrap.c b/src/bootstrap.c index 244ecc1..8673cd7 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -40,7 +40,7 @@ extern struct arg_opts arg_opts; /* URL that we get the JSON encoded nodes list from. */ #define NODES_LIST_URL "https://nodes.tox.chat/json" -#define DEFAULT_NODES_FILENAME "DHTnodes" +#define DEFAULT_NODES_FILENAME "DHTnodes.json" /* Time to wait between bootstrap attempts */ #define TRY_BOOTSTRAP_INTERVAL 5 From 151f5f0c51b15802c38f2204cf7188190da5a3d6 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Tue, 20 Sep 2016 13:13:12 -0400 Subject: [PATCH 16/23] Add setting to control DHT nodeslist update frequency Also rename a few things and semi-fix man page format issues --- doc/toxic.1 | 6 ++++-- doc/toxic.1.asc | 4 ++-- doc/toxic.conf.5 | 25 +++++++++++++++------ doc/toxic.conf.5.asc | 11 ++++++---- misc/toxic.conf.example | 8 ++++--- src/bootstrap.c | 48 +++++++++++++++++++++-------------------- src/curl_util.c | 4 ++-- src/curl_util.h | 4 ++-- src/name_lookup.c | 8 +++---- src/settings.c | 7 +++++- src/settings.h | 1 + 11 files changed, 77 insertions(+), 49 deletions(-) diff --git a/doc/toxic.1 b/doc/toxic.1 index 22101af..fad14a9 100644 --- a/doc/toxic.1 +++ b/doc/toxic.1 @@ -82,7 +82,8 @@ Show help message .RS 4 Use specified \fInodes\-file\fR -for DHT bootstrap nodes, instead of the default +for DHT bootstrap nodes instead of +\fI~/\&.config/tox/DHTnodes\&.json\fR .RE .PP \-o, \-\-noconnect @@ -124,7 +125,8 @@ Unencrypt a data file\&. A warning will appear if this option is used with a dat ~/\&.config/tox/DHTnodes\&.json .RS 4 Default location for list of DHT bootstrap nodes (list obtained from -https://nodes\&.tox\&.chat)\&. This list is automatically updated every 30 days\&. +https://nodes\&.tox\&.chat)\&. This list is automatically updated\&. See +\fBtoxic\&.conf\fR(5) for details on controlling the update frequency\&. .RE .PP ~/\&.config/tox/toxic_profile\&.tox diff --git a/doc/toxic.1.asc b/doc/toxic.1.asc index 9abd941..dcfc01a 100644 --- a/doc/toxic.1.asc +++ b/doc/toxic.1.asc @@ -41,7 +41,7 @@ OPTIONS Show help message -n, --nodes nodes-file:: - Use specified 'nodes-file' for DHT bootstrap nodes, instead of the default + Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' -o, --noconnect:: Do not connect to the DHT network @@ -69,7 +69,7 @@ FILES ----- ~/.config/tox/DHTnodes.json:: Default location for list of DHT bootstrap nodes (list obtained from https://nodes.tox.chat). - This list is automatically updated every 30 days. + This list is automatically updated. See *toxic.conf*(5) for details on controlling the update frequency. ~/.config/tox/toxic_profile.tox:: Savestate which contains your personal info (nickname, Tox ID, contacts, diff --git a/doc/toxic.conf.5 b/doc/toxic.conf.5 index 40c1d75..7af3bf5 100644 --- a/doc/toxic.conf.5 +++ b/doc/toxic.conf.5 @@ -2,12 +2,12 @@ .\" Title: toxic.conf .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.78.1 -.\" Date: 2016-02-28 +.\" Date: 2016-07-21 .\" Manual: Toxic Manual .\" Source: toxic __VERSION__ .\" Language: English .\" -.TH "TOXIC\&.CONF" "5" "2016\-02\-28" "toxic __VERSION__" "Toxic Manual" +.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -118,6 +118,11 @@ Show welcome message on startup\&. true or false Enable friend connection change notifications\&. true or false .RE .PP +\fBnodelist_update_freq\fR +.RS 4 +How often in days to update the DHT nodes list\&. (0 to disable updates) +.RE +.PP \fBhistory_size\fR .RS 4 Maximum lines for chat window history\&. Integer value\&. (for example: 700) @@ -151,11 +156,19 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or \fBmplex_away_note\fR .RS 4 Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&. -.RE -.PP +.sp +.if n \{\ +.RS 4 +.\} +.nf The following options control whether to output a terminal bell on certain events\&. -.br -Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. These options don't affect the "alerts" option\&. +Some terminals mark the window as urgent when a bell is received\&. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window\&. +These options don\*(Aqt affect the "alerts" option\&. +.fi +.if n \{\ +.RE +.\} +.RE .PP \fBbell_on_message\fR .RS 4 diff --git a/doc/toxic.conf.5.asc b/doc/toxic.conf.5.asc index 8e0b22f..dd931ec 100644 --- a/doc/toxic.conf.5.asc +++ b/doc/toxic.conf.5.asc @@ -75,6 +75,9 @@ OPTIONS *show_connection_msg*;; Enable friend connection change notifications. true or false + *nodelist_update_freq*;; + How often in days to update the DHT nodes list. (0 to disable updates) + *history_size*;; Maximum lines for chat window history. Integer value. (for example: 700) @@ -104,16 +107,16 @@ OPTIONS Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window. These options don't affect the "alerts" option. - *bell_on_message* + *bell_on_message*;; Enable/Disable the terminal bell when receiving a message. true or false - *bell_on_filetrans* + *bell_on_filetrans*;; Enable/Disable the terminal bell when receiving a filetransfer. true or false - *bell_on_filetrans_accept* + *bell_on_filetrans_accept*;; Enable/Disable the terminal bell when a filetransfer was accepted. true or false - *bell_on_invite* + *bell_on_invite*;; Enable/Disable the terminal bell when receiving a group/call invite. true or false diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index c162cdf..8791da9 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -29,7 +29,7 @@ ui = { // 24 or 12 hour time time_format=24; - // timestamp format string according to date/strftime format. Overrides time_format setting + // Timestamp format string according to date/strftime format. Overrides time_format setting timestamp_format="%H:%M:%S"; // true to show you when others are typing a message in 1-on-1 chats @@ -44,6 +44,9 @@ ui = { // true to show friend connection change messages on the home screen show_connection_msg=true; + // How often in days to update the DHT nodes list. (0 to disable updates) + nodeslist_update_freq=30; + // maximum lines for chat window history history_size=700; @@ -101,8 +104,7 @@ sounds = { }; // Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive) -// Note: All printable keys register as input -// Note2: Ctrl+M does not work +// Note: Ctrl+M does not work keys = { next_tab="Ctrl+P"; prev_tab="Ctrl+O"; diff --git a/src/bootstrap.c b/src/bootstrap.c index 8673cd7..eb259ab 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -33,9 +33,10 @@ #include "misc_tools.h" #include "configdir.h" #include "curl_util.h" - +#include "settings.h" extern struct arg_opts arg_opts; +extern struct user_settings *user_settings; /* URL that we get the JSON encoded nodes list from. */ #define NODES_LIST_URL "https://nodes.tox.chat/json" @@ -48,9 +49,6 @@ extern struct arg_opts arg_opts; /* Number of nodes to bootstrap to per try */ #define NUM_BOOTSTRAP_NODES 5 -/* How often we should update the nodeslist file. */ -#define NODELIST_UPDATE_TIMEOUT (60*24*30) - #define IPv4_MAX_SIZE 64 #define PORT_MAX_SIZE 5 @@ -72,12 +70,12 @@ extern struct arg_opts arg_opts; #define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7) #define MAX_NODELIST_SIZE (1024 * MAXNODES) -static struct toxNodes { +static struct DHT_Nodes { size_t lines; char nodes[MAXNODES][NODELEN]; uint16_t ports[MAXNODES]; char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; -} toxNodes; +} Nodes; /* Return true if nodeslist pointed to by fp needs to be updated. @@ -86,6 +84,10 @@ static struct toxNodes { */ static bool nodeslist_needs_update(const char *nodes_path) { + if (user_settings->nodeslist_update_freq <= 0) { + return false; + } + FILE *fp = fopen(nodes_path, "r+"); if (fp == NULL) { @@ -111,7 +113,7 @@ static bool nodeslist_needs_update(const char *nodes_path) last_scan_val += LAST_SCAN_JSON_VALUE_LEN; long long int last_scan = strtoll(last_scan_val, NULL, 10); - if (timed_out(last_scan, NODELIST_UPDATE_TIMEOUT)) { + if (timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60)) { return true; } @@ -123,7 +125,7 @@ static bool nodeslist_needs_update(const char *nodes_path) * Return 0 on success. * Return -1 on failure. */ -static int curl_fetch_nodes_JSON(struct Recv_Data *recv_data) +static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data) { CURL *c_handle = curl_easy_init(); @@ -137,7 +139,7 @@ static int curl_fetch_nodes_JSON(struct Recv_Data *recv_data) curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(c_handle, CURLOPT_URL, NODES_LIST_URL); - curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, write_lookup_data); + curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data); curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); curl_easy_setopt(c_handle, CURLOPT_HTTPGET, 1L); @@ -202,8 +204,8 @@ static int update_DHT_nodeslist(const char *nodes_path) return -1; } - struct Recv_Data recv_data; - memset(&recv_data, 0, sizeof(struct Recv_Data)); + struct Recv_Curl_Data recv_data; + memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); if (curl_fetch_nodes_JSON(&recv_data) == -1) { fclose(fp); @@ -276,7 +278,7 @@ int load_DHT_nodeslist(void) const char *line_start = line; - while ((line_start = strstr(line_start + 1, IPV4_JSON_VALUE)) && toxNodes.lines < MAXNODES) { + while ((line_start = strstr(line_start + 1, IPV4_JSON_VALUE)) && Nodes.lines < MAXNODES) { /* Extract IPv4 address */ const char *ip_start = strstr(line_start, IPV4_JSON_VALUE); @@ -338,19 +340,19 @@ int load_DHT_nodeslist(void) key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; /* Add entry to nodes list */ - snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", ipv4_string); - toxNodes.ports[toxNodes.lines] = port; + snprintf(Nodes.nodes[Nodes.lines], sizeof(Nodes.nodes[Nodes.lines]), "%s", ipv4_string); + Nodes.ports[Nodes.lines] = port; - if (hex_string_to_bin(key_string, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) + if (hex_string_to_bin(key_string, key_len, Nodes.keys[Nodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) continue; - toxNodes.lines++; + Nodes.lines++; } /* If nodeslist does not contain any valid entries we set the last_scan value * to 0 so that it will fetch a new list the next time this function is called. */ - if (toxNodes.lines == 0) { + if (Nodes.lines == 0) { const char *s = "{\"last_scan\":0}"; rewind(fp); fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails @@ -365,26 +367,26 @@ int load_DHT_nodeslist(void) /* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ static void DHT_bootstrap(Tox *m) { - if (toxNodes.lines == 0) { + if (Nodes.lines == 0) { return; } size_t i; for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { - size_t node = rand() % toxNodes.lines; + size_t node = rand() % Nodes.lines; TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + tox_bootstrap(m, Nodes.nodes[node], Nodes.ports[node], (uint8_t *) Nodes.keys[node], &err); if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + fprintf(stderr, "Failed to bootstrap %s:%d\n", Nodes.nodes[node], Nodes.ports[node]); } - tox_add_tcp_relay(m, toxNodes.nodes[node], toxNodes.ports[node], (uint8_t *) toxNodes.keys[node], &err); + tox_add_tcp_relay(m, Nodes.nodes[node], Nodes.ports[node], (uint8_t *) Nodes.keys[node], &err); if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[node], toxNodes.ports[node]); + fprintf(stderr, "Failed to add TCP relay %s:%d\n", Nodes.nodes[node], Nodes.ports[node]); } } } diff --git a/src/curl_util.c b/src/curl_util.c index 5ab2944..0678517 100644 --- a/src/curl_util.c +++ b/src/curl_util.c @@ -74,9 +74,9 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin * Returns number of bytes received from http request on success (don't change this). * Returns 0 if data exceeds buffer size. */ -size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer) +size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer) { - struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer; + struct Recv_Curl_Data *recv_data = (struct Recv_Curl_Data *) user_pointer; size_t length = size * nmemb; size_t total_size = length + recv_data->length; diff --git a/src/curl_util.h b/src/curl_util.h index b6b67f1..e38da56 100644 --- a/src/curl_util.h +++ b/src/curl_util.h @@ -27,7 +27,7 @@ #define MAX_RECV_CURL_DATA_SIZE 32767 /* Holds data received from curl lookup */ -struct Recv_Data { +struct Recv_Curl_Data { char data[MAX_RECV_CURL_DATA_SIZE + 1]; /* Data received from curl write data callback */ size_t length; /* Total number of bytes written to data buffer (doesn't include null) */ }; @@ -47,4 +47,4 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin * * Returns size of bytes written to the data buffer. */ -size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer); +size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); diff --git a/src/name_lookup.c b/src/name_lookup.c index 863e9aa..b78d3f4 100644 --- a/src/name_lookup.c +++ b/src/name_lookup.c @@ -192,7 +192,7 @@ static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_s * Returns -1 on failure. */ #define ID_PREFIX "\"tox_id\": \"" -static int process_response(struct Recv_Data *recv_data) +static int process_response(struct Recv_Curl_Data *recv_data) { size_t prefix_size = strlen(ID_PREFIX); @@ -248,8 +248,8 @@ void *lookup_thread_func(void *data) kill_lookup_thread(); } - struct Recv_Data recv_data; - memset(&recv_data, 0, sizeof(struct Recv_Data)); + struct Recv_Curl_Data recv_data; + memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); char post_data[MAX_STR_SIZE]; snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name); @@ -261,7 +261,7 @@ void *lookup_thread_func(void *data) curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(c_handle, CURLOPT_URL, real_domain); - curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, write_lookup_data); + curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data); curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data); curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data); diff --git a/src/settings.c b/src/settings.c index cef92a2..56525a2 100644 --- a/src/settings.c +++ b/src/settings.c @@ -62,6 +62,7 @@ static struct ui_strings { const char* show_typing_other; const char* show_welcome_msg; const char* show_connection_msg; + const char* nodeslist_update_freq; const char* line_join; const char* line_quit; @@ -88,6 +89,7 @@ static struct ui_strings { "show_typing_other", "show_welcome_msg", "show_connection_msg", + "nodeslist_update_freq", "line_join", "line_quit", "line_alert", @@ -114,6 +116,7 @@ static void ui_defaults(struct user_settings* settings) settings->show_typing_other = SHOW_TYPING_ON; settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; settings->show_connection_msg = SHOW_CONNECTION_MSG_ON; + settings->nodeslist_update_freq = 30; snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); @@ -347,12 +350,14 @@ int settings_load(struct user_settings *s, const char *patharg) config_setting_lookup_bool(setting, ui_strings.autolog, &s->autolog); config_setting_lookup_bool(setting, ui_strings.native_colors, &s->colour_theme); - config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self); config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other); config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg); config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg); + config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); + config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq); + if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { snprintf(s->line_join, sizeof(s->line_join), "%s", str); } diff --git a/src/settings.h b/src/settings.h index a195ad0..91228a7 100644 --- a/src/settings.h +++ b/src/settings.h @@ -53,6 +53,7 @@ struct user_settings { int show_typing_other; /* boolean */ int show_welcome_msg; /* boolean */ int show_connection_msg; /* boolean */ + int nodeslist_update_freq; /* int (<= 0 to disable updates) */ char line_join[LINE_HINT_MAX + 1]; char line_quit[LINE_HINT_MAX + 1]; From 221d761ff49f3e6fb3e9ae0a0be49366054d7339 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Wed, 21 Sep 2016 00:28:16 -0400 Subject: [PATCH 17/23] Ignore bootstrap nodes that use a domain instead of IP address Domains cause toxcore to do blocking DNS requests which creates noticable lag and might (??) leak IP addresses when using a proxy --- src/bootstrap.c | 6 +++++- src/misc_tools.c | 9 +++++++++ src/misc_tools.h | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/bootstrap.c b/src/bootstrap.c index eb259ab..6d807b8 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -77,7 +77,6 @@ static struct DHT_Nodes { char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; } Nodes; - /* Return true if nodeslist pointed to by fp needs to be updated. * This will be the case if the file is empty, has an invalid format, * or if the file is older than the given timeout. @@ -297,6 +296,11 @@ int load_DHT_nodeslist(void) memcpy(ipv4_string, ip_start, ip_len); ipv4_string[ip_len] = 0; + /* ignore domains because we don't want toxcore doing DNS requests during bootstrap. */ + if (!is_ip4_address(ipv4_string)) { + continue; + } + /* Extract port */ const char *port_start = strstr(ip_start, PORT_JSON_VALUE); diff --git a/src/misc_tools.c b/src/misc_tools.c index 438e748..98bfdaf 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -26,7 +26,9 @@ #include #include #include + #include +#include #include "toxic.h" #include "windows.h" @@ -478,3 +480,10 @@ void set_window_title(ToxWindow *self, const char *title, int len) snprintf(self->name, sizeof(self->name), "%s", cpy); } + +/* Return true if address appears to be a valid ipv4 address. */ +bool is_ip4_address(const char *address) +{ + struct sockaddr_in s_addr; + return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; +} diff --git a/src/misc_tools.h b/src/misc_tools.h index 8d109a3..dbd4277 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -161,4 +161,7 @@ int check_file_signature(const char *signature, size_t size, FILE *fp); /* sets window title in tab bar. */ void set_window_title(ToxWindow *self, const char *title, int len); +/* Return true if address appears to be a valid ipv4 address. */ +bool is_ip4_address(const char *address); + #endif /* #define MISC_TOOLS_H */ From 703d5419a3c82dd16a58751b45b2dd0018881e62 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Wed, 21 Sep 2016 12:51:57 -0400 Subject: [PATCH 18/23] Ignore offline bootstrap nodes, small refactor of Nodes struct --- misc/toxic.conf.example | 2 +- src/bootstrap.c | 113 +++++++++++++++++++++++++--------------- src/settings.c | 2 +- src/toxic.c | 2 +- 4 files changed, 75 insertions(+), 44 deletions(-) diff --git a/misc/toxic.conf.example b/misc/toxic.conf.example index 8791da9..9e5242b 100644 --- a/misc/toxic.conf.example +++ b/misc/toxic.conf.example @@ -45,7 +45,7 @@ ui = { show_connection_msg=true; // How often in days to update the DHT nodes list. (0 to disable updates) - nodeslist_update_freq=30; + nodeslist_update_freq=7; // maximum lines for chat window history history_size=700; diff --git a/src/bootstrap.c b/src/bootstrap.c index 6d807b8..22aa5f0 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -49,34 +49,51 @@ extern struct user_settings *user_settings; /* Number of nodes to bootstrap to per try */ #define NUM_BOOTSTRAP_NODES 5 -#define IPv4_MAX_SIZE 64 +/* Number of seconds since last successful ping before we consider a node offline */ +#define NODE_OFFLINE_TIMOUT (60*60*24*2) + +#define IP_MAX_SIZE 45 #define PORT_MAX_SIZE 5 -#define LAST_SCAN_JSON_VALUE "\"last_scan\":" -#define LAST_SCAN_JSON_VALUE_LEN (sizeof(LAST_SCAN_JSON_VALUE) - 1) +#define LAST_SCAN_JSON_KEY "\"last_scan\":" +#define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1) -#define IPV4_JSON_VALUE "\"ipv4\":\"" -#define IPV4_JSON_VALUE_LEN (sizeof(IPV4_JSON_VALUE) - 1) +#define IPV4_JSON_KEY "\"ipv4\":\"" +#define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1) -#define PORT_JSON_VALUE "\"port\":" -#define PORT_JSON_VALUE_LEN (sizeof(PORT_JSON_VALUE) - 1) +#define PORT_JSON_KEY "\"port\":" +#define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1) -#define KEY_JSON_VALUE "\"public_key\":\"" -#define KEY_JSON_VALUE_LEN (sizeof(KEY_JSON_VALUE) - 1) +#define PK_JSON_KEY "\"public_key\":\"" +#define PK_JSON_KEY_LEN (sizeof(PK_JSON_KEY) - 1) + +#define LAST_PING_JSON_KEY "\"last_ping\":" +#define LAST_PING_JSON_KEY_LEN (sizeof(LAST_PING_JSON_KEY) - 1) + +/* Maximum allowable size of the nodes list */ +#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE) -#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */ -#define MAX_NODE_LINE 300 /* Max number of chars in a sever line (name + port + key) */ #define MAXNODES 50 -#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7) -#define MAX_NODELIST_SIZE (1024 * MAXNODES) +struct Node { + char ip4[IP_MAX_SIZE + 1]; + char ip6[IP_MAX_SIZE + 1]; + char key[TOX_PUBLIC_KEY_SIZE]; + uint16_t port; +}; static struct DHT_Nodes { - size_t lines; - char nodes[MAXNODES][NODELEN]; - uint16_t ports[MAXNODES]; - char keys[MAXNODES][TOX_PUBLIC_KEY_SIZE]; + struct Node list[MAXNODES]; + size_t count; + uint64_t last_updated; } Nodes; + +/* Determine if a node is offline by comparing the age of the nodeslist + * to the last time the node was successfully pinged. + */ +#define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping)) + + /* Return true if nodeslist pointed to by fp needs to be updated. * This will be the case if the file is empty, has an invalid format, * or if the file is older than the given timeout. @@ -94,7 +111,7 @@ static bool nodeslist_needs_update(const char *nodes_path) } /* last_scan value should be at beginning of file */ - char line[LAST_SCAN_JSON_VALUE_LEN + 32]; + char line[LAST_SCAN_JSON_KEY_LEN + 32]; if (fgets(line, sizeof(line), fp) == NULL) { fclose(fp); @@ -103,14 +120,15 @@ static bool nodeslist_needs_update(const char *nodes_path) fclose(fp); - const char *last_scan_val = strstr(line, LAST_SCAN_JSON_VALUE); + const char *last_scan_val = strstr(line, LAST_SCAN_JSON_KEY); if (last_scan_val == NULL) { return true; } - last_scan_val += LAST_SCAN_JSON_VALUE_LEN; + last_scan_val += LAST_SCAN_JSON_KEY_LEN; long long int last_scan = strtoll(last_scan_val, NULL, 10); + Nodes.last_updated = last_scan; if (timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60)) { return true; @@ -268,7 +286,7 @@ int load_DHT_nodeslist(void) fprintf(stderr, "update_DHT_nodeslist() failed with error %d\n", update_err); } - char line[MAX_NODELIST_SIZE]; + char line[MAX_NODELIST_SIZE + 1]; if (fgets(line, sizeof(line), fp) == NULL) { fclose(fp); @@ -277,18 +295,18 @@ int load_DHT_nodeslist(void) const char *line_start = line; - while ((line_start = strstr(line_start + 1, IPV4_JSON_VALUE)) && Nodes.lines < MAXNODES) { + while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY)) && Nodes.count < MAXNODES) { /* Extract IPv4 address */ - const char *ip_start = strstr(line_start, IPV4_JSON_VALUE); + const char *ip_start = strstr(line_start, IPV4_JSON_KEY); if (ip_start == NULL) { continue; } - ip_start += IPV4_JSON_VALUE_LEN; + ip_start += IPV4_JSON_KEY_LEN; int ip_len = char_find(0, ip_start, '"'); - if (ip_len == 0 || ip_len > IPv4_MAX_SIZE) { + if (ip_len == 0 || ip_len > IP_MAX_SIZE) { continue; } @@ -302,13 +320,13 @@ int load_DHT_nodeslist(void) } /* Extract port */ - const char *port_start = strstr(ip_start, PORT_JSON_VALUE); + const char *port_start = strstr(ip_start, PORT_JSON_KEY); if (!port_start) { continue; } - port_start += PORT_JSON_VALUE_LEN; + port_start += PORT_JSON_KEY_LEN; int port_len = char_find(0, port_start, ','); if (port_len == 0 || port_len > PORT_MAX_SIZE) { @@ -326,13 +344,13 @@ int load_DHT_nodeslist(void) } /* Extract key */ - const char *key_start = strstr(port_start, KEY_JSON_VALUE); + const char *key_start = strstr(port_start, PK_JSON_KEY); if (!key_start) { continue; } - key_start += KEY_JSON_VALUE_LEN; + key_start += PK_JSON_KEY_LEN; int key_len = char_find(0, key_start, '"'); if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { @@ -343,20 +361,33 @@ int load_DHT_nodeslist(void) memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2); key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; - /* Add entry to nodes list */ - snprintf(Nodes.nodes[Nodes.lines], sizeof(Nodes.nodes[Nodes.lines]), "%s", ipv4_string); - Nodes.ports[Nodes.lines] = port; + /* Check last pinged value and ignore nodes that appear offline */ + const char *last_pinged_str = strstr(key_start, LAST_PING_JSON_KEY); - if (hex_string_to_bin(key_string, key_len, Nodes.keys[Nodes.lines], TOX_PUBLIC_KEY_SIZE) == -1) + if (!last_pinged_str) { continue; + } - Nodes.lines++; + last_pinged_str += LAST_PING_JSON_KEY_LEN; + long long int last_pinged = strtoll(last_pinged_str, NULL, 10); + + if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { + continue; + } + + /* Add entry to nodes list */ + size_t idx = Nodes.count++; + snprintf(Nodes.list[idx].ip4, sizeof(Nodes.list[idx].ip4), "%s", ipv4_string); + Nodes.list[idx].port = port; + + if (hex_string_to_bin(key_string, key_len, Nodes.list[idx].key, TOX_PUBLIC_KEY_SIZE) == -1) + continue; } /* If nodeslist does not contain any valid entries we set the last_scan value * to 0 so that it will fetch a new list the next time this function is called. */ - if (Nodes.lines == 0) { + if (Nodes.count == 0) { const char *s = "{\"last_scan\":0}"; rewind(fp); fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails @@ -371,26 +402,26 @@ int load_DHT_nodeslist(void) /* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ static void DHT_bootstrap(Tox *m) { - if (Nodes.lines == 0) { + if (Nodes.count == 0) { return; } size_t i; for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { - size_t node = rand() % Nodes.lines; + size_t node = rand() % Nodes.count; TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, Nodes.nodes[node], Nodes.ports[node], (uint8_t *) Nodes.keys[node], &err); + tox_bootstrap(m, Nodes.list[node].ip4, Nodes.list[node].port, (uint8_t *) Nodes.list[node].key, &err); if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to bootstrap %s:%d\n", Nodes.nodes[node], Nodes.ports[node]); + fprintf(stderr, "Failed to bootstrap %s:%d\n", Nodes.list[node].ip4, Nodes.list[node].port); } - tox_add_tcp_relay(m, Nodes.nodes[node], Nodes.ports[node], (uint8_t *) Nodes.keys[node], &err); + tox_add_tcp_relay(m, Nodes.list[node].ip4, Nodes.list[node].port, (uint8_t *) Nodes.list[node].key, &err); if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to add TCP relay %s:%d\n", Nodes.nodes[node], Nodes.ports[node]); + fprintf(stderr, "Failed to add TCP relay %s:%d\n", Nodes.list[node].ip4, Nodes.list[node].port); } } } diff --git a/src/settings.c b/src/settings.c index 56525a2..3e8e8df 100644 --- a/src/settings.c +++ b/src/settings.c @@ -116,7 +116,7 @@ static void ui_defaults(struct user_settings* settings) settings->show_typing_other = SHOW_TYPING_ON; settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; settings->show_connection_msg = SHOW_CONNECTION_MSG_ON; - settings->nodeslist_update_freq = 30; + settings->nodeslist_update_freq = 7; snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); diff --git a/src/toxic.c b/src/toxic.c index 2691656..f064eb7 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -1143,7 +1143,7 @@ int main(int argc, char **argv) int nodeslist_ret = load_DHT_nodeslist(); if (nodeslist_ret != 0) { - queue_init_message("DHT nodeslist failed to load (error %d)\n", nodeslist_ret); + queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret); } #ifdef X11 From d2b572ede15d4b1684e1fdc15638f144cdbfd416 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Wed, 21 Sep 2016 21:22:05 -0400 Subject: [PATCH 19/23] Add ipv6 support for bootstrap nodes and refactor parsing code --- src/bootstrap.c | 250 ++++++++++++++++++++++++++++++----------------- src/bootstrap.h | 5 + src/curl_util.h | 5 + src/misc_tools.c | 26 +++++ src/misc_tools.h | 9 ++ 5 files changed, 204 insertions(+), 91 deletions(-) diff --git a/src/bootstrap.c b/src/bootstrap.c index 22aa5f0..51fd760 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -53,6 +53,7 @@ extern struct user_settings *user_settings; #define NODE_OFFLINE_TIMOUT (60*60*24*2) #define IP_MAX_SIZE 45 +#define IP_MIN_SIZE 7 #define PORT_MAX_SIZE 5 #define LAST_SCAN_JSON_KEY "\"last_scan\":" @@ -61,6 +62,9 @@ extern struct user_settings *user_settings; #define IPV4_JSON_KEY "\"ipv4\":\"" #define IPV4_JSON_KEY_LEN (sizeof(IPV4_JSON_KEY) - 1) +#define IPV6_JSON_KEY "\"ipv6\":\"" +#define IPV6_JSON_KEY_LEN (sizeof(IPV6_JSON_KEY) - 1) + #define PORT_JSON_KEY "\"port\":" #define PORT_JSON_KEY_LEN (sizeof(PORT_JSON_KEY) - 1) @@ -76,7 +80,11 @@ extern struct user_settings *user_settings; #define MAXNODES 50 struct Node { char ip4[IP_MAX_SIZE + 1]; + bool have_ip4; + char ip6[IP_MAX_SIZE + 1]; + bool have_ip6; + char key[TOX_PUBLIC_KEY_SIZE]; uint16_t port; }; @@ -257,6 +265,142 @@ static void get_nodeslist_path(char *buf, size_t buf_size) } } +/* Return true if json encoded string s contains a valid IP address and puts address in ip_buf. + * + * ip_type should be set to 1 for ipv4 address, or 0 for ipv6 addresses. + * ip_buf must have room for at least IP_MAX_SIZE + 1 bytes. + */ +static bool extract_val_ip(const char *s, char *ip_buf, unsigned short int ip_type) +{ + int ip_len = char_find(0, s, '"'); + + if (ip_len < IP_MIN_SIZE || ip_len > IP_MAX_SIZE) { + return false; + } + + memcpy(ip_buf, s, ip_len); + ip_buf[ip_len] = 0; + + return (ip_type == 1) ? is_ip4_address(ip_buf) : is_ip6_address(ip_buf); +} + +/* Extracts the port from json encoded string s. + * + * Return port number on success. + * Return 0 on failure. + */ +static uint16_t extract_val_port(const char *s) +{ + long int port = strtol(s, NULL, 10); + return (port > 0 && port <= MAX_PORT_RANGE) ? port : 0; +} + +/* Extracts the last pinged value from json encoded string s. + * + * Return timestamp on success. + * Return -1 on failure. + */ +static long long int extract_val_last_pinged(const char *s) +{ + long long int last_pinged = strtoll(s, NULL, 10); + return (last_pinged <= 0) ? -1 : last_pinged; +} + +/* Extracts DHT public key from json encoded string s and puts key in key_buf. + * key_buf must have room for at least TOX_PUBLIC_KEY_SIZE * 2 + 1 bytes. + * + * Return number of bytes copied to key_buf on success. + * Return -1 on failure. + */ +static int extract_val_pk(const char *s, char *key_buf) +{ + + int key_len = char_find(0, s, '"'); + + if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { + return -1; + } + + memcpy(key_buf, s, key_len); + key_buf[key_len] = 0; + + return key_len; +} + +/* Extracts values from json formatted string, validats them, and puts them in node. + * + * Return 0 on success. + * Return -1 if line is empty. + * Return -2 if line does not appear to be a valid nodes list entry. + * Return -3 if node appears to be offline. + * Return -4 if entry does not contain either a valid ipv4 or ipv6 address. + * Return -5 if port value is invalid. + * Return -6 if public key is invalid. + */ +static int extract_node(const char *line, struct Node *node) +{ + if (!line) { + return -1; + } + + const char *ip4_start = strstr(line, IPV4_JSON_KEY); + const char *ip6_start = strstr(line, IPV6_JSON_KEY); + const char *port_start = strstr(line, PORT_JSON_KEY); + const char *key_start = strstr(line, PK_JSON_KEY); + const char *last_pinged_str = strstr(line, LAST_PING_JSON_KEY); + + if (!ip4_start || !ip6_start || !port_start || !key_start || !last_pinged_str) { + return -2; + } + + long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN); + + if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { + return -3; + } + + char ip4_string[IP_MAX_SIZE + 1]; + bool have_ip4 = extract_val_ip(ip4_start + IPV4_JSON_KEY_LEN, ip4_string, 1); + + char ip6_string[IP_MAX_SIZE + 1]; + bool have_ip6 = extract_val_ip(ip6_start + IPV6_JSON_KEY_LEN, ip6_string, 0); + + if (!have_ip6 && !have_ip4) { + return -4; + } + + uint16_t port = extract_val_port(port_start + PORT_JSON_KEY_LEN); + + if (port == 0) { + return -5; + } + + char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; + int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string); + + if (key_len == -1) { + return -6; + } + + if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) { + return -6; + } + + if (have_ip4) { + snprintf(node->ip4, sizeof(node->ip4), "%s", ip4_string); + node->have_ip4 = true; + } + + if (have_ip6) { + snprintf(node->ip6, sizeof(node->ip6), "%s", ip6_string); + node->have_ip6 = true; + } + + node->port = port; + + return 0; +} + /* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. * TODO: Parse json using a proper library? * @@ -296,92 +440,11 @@ int load_DHT_nodeslist(void) const char *line_start = line; while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY)) && Nodes.count < MAXNODES) { - /* Extract IPv4 address */ - const char *ip_start = strstr(line_start, IPV4_JSON_KEY); + size_t idx = Nodes.count; - if (ip_start == NULL) { - continue; + if (extract_node(line_start, &Nodes.list[idx]) == 0) { + ++Nodes.count; } - - ip_start += IPV4_JSON_KEY_LEN; - int ip_len = char_find(0, ip_start, '"'); - - if (ip_len == 0 || ip_len > IP_MAX_SIZE) { - continue; - } - - char ipv4_string[ip_len + 1]; - memcpy(ipv4_string, ip_start, ip_len); - ipv4_string[ip_len] = 0; - - /* ignore domains because we don't want toxcore doing DNS requests during bootstrap. */ - if (!is_ip4_address(ipv4_string)) { - continue; - } - - /* Extract port */ - const char *port_start = strstr(ip_start, PORT_JSON_KEY); - - if (!port_start) { - continue; - } - - port_start += PORT_JSON_KEY_LEN; - int port_len = char_find(0, port_start, ','); - - if (port_len == 0 || port_len > PORT_MAX_SIZE) { - continue; - } - - char port_string[port_len + 1]; - memcpy(port_string, port_start, port_len); - port_string[port_len] = 0; - - long int port = strtol(port_string, NULL, 10); - - if (port <= 0 || port > MAX_PORT_RANGE) { - continue; - } - - /* Extract key */ - const char *key_start = strstr(port_start, PK_JSON_KEY); - - if (!key_start) { - continue; - } - - key_start += PK_JSON_KEY_LEN; - int key_len = char_find(0, key_start, '"'); - - if (key_len != TOX_PUBLIC_KEY_SIZE * 2) { - continue; - } - - char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1]; - memcpy(key_string, key_start, TOX_PUBLIC_KEY_SIZE * 2); - key_string[TOX_PUBLIC_KEY_SIZE * 2] = 0; - - /* Check last pinged value and ignore nodes that appear offline */ - const char *last_pinged_str = strstr(key_start, LAST_PING_JSON_KEY); - - if (!last_pinged_str) { - continue; - } - - last_pinged_str += LAST_PING_JSON_KEY_LEN; - long long int last_pinged = strtoll(last_pinged_str, NULL, 10); - - if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { - continue; - } - - /* Add entry to nodes list */ - size_t idx = Nodes.count++; - snprintf(Nodes.list[idx].ip4, sizeof(Nodes.list[idx].ip4), "%s", ipv4_string); - Nodes.list[idx].port = port; - - if (hex_string_to_bin(key_string, key_len, Nodes.list[idx].key, TOX_PUBLIC_KEY_SIZE) == -1) - continue; } /* If nodeslist does not contain any valid entries we set the last_scan value @@ -409,19 +472,24 @@ static void DHT_bootstrap(Tox *m) size_t i; for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { - size_t node = rand() % Nodes.count; - TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, Nodes.list[node].ip4, Nodes.list[node].port, (uint8_t *) Nodes.list[node].key, &err); + struct Node *node = &Nodes.list[rand() % Nodes.count]; + const char *addr = node->have_ip4 ? node->ip4 : node->ip6; - if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to bootstrap %s:%d\n", Nodes.list[node].ip4, Nodes.list[node].port); + if (!addr) { + continue; } - tox_add_tcp_relay(m, Nodes.list[node].ip4, Nodes.list[node].port, (uint8_t *) Nodes.list[node].key, &err); + tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); if (err != TOX_ERR_BOOTSTRAP_OK) { - fprintf(stderr, "Failed to add TCP relay %s:%d\n", Nodes.list[node].ip4, Nodes.list[node].port); + fprintf(stderr, "Failed to bootstrap %s:%d\n", addr, node->port); + } + + tox_add_tcp_relay(m, addr, node->port, (uint8_t *) node->key, &err); + + if (err != TOX_ERR_BOOTSTRAP_OK) { + fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port); } } } diff --git a/src/bootstrap.h b/src/bootstrap.h index 0159d3d..b711a24 100644 --- a/src/bootstrap.h +++ b/src/bootstrap.h @@ -20,6 +20,9 @@ * */ +#ifndef BOOTSTRAP_H +#define BOOTSTRAP_H + /* Manages connection to the Tox DHT network. */ void do_tox_connection(Tox *m); @@ -32,3 +35,5 @@ void do_tox_connection(Tox *m); * Return -3 if nodeslist file does not contain any valid node entries. */ int load_DHT_nodeslist(void); + +#endif /* BOOTSTRAP_H */ diff --git a/src/curl_util.h b/src/curl_util.h index e38da56..cefb5db 100644 --- a/src/curl_util.h +++ b/src/curl_util.h @@ -20,6 +20,9 @@ * */ +#ifndef CURL_UTIL_H +#define CURL_UTIL_H + /* List based on Mozilla's recommended configurations for modern browsers */ #define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK" @@ -48,3 +51,5 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin * Returns size of bytes written to the data buffer. */ size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); + +#endif /* CURL_UTIL_H */ diff --git a/src/misc_tools.c b/src/misc_tools.c index 98bfdaf..8602538 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -487,3 +487,29 @@ bool is_ip4_address(const char *address) struct sockaddr_in s_addr; return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0; } + +/* Return true if address roughly appears to be a valid ipv6 address. + * + * TODO: Improve this function (inet_pton behaves strangely with ipv6). + * for now the only guarantee is that it won't return true if the + * address is a domain or ipv4 address, and should only be used if you're + * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). + */ +bool is_ip6_address(const char *address) +{ + size_t i; + size_t num_colons = 0; + char ch = 0; + + for (i = 0; (ch = address[i]); ++i) { + if (ch == '.') { + return false; + } + + if (ch == ':') { + ++num_colons; + } + } + + return num_colons > 1 && num_colons < 8; +} diff --git a/src/misc_tools.h b/src/misc_tools.h index dbd4277..ef450c9 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -164,4 +164,13 @@ void set_window_title(ToxWindow *self, const char *title, int len); /* Return true if address appears to be a valid ipv4 address. */ bool is_ip4_address(const char *address); +/* Return true if address roughly appears to be a valid ipv6 address. + * + * TODO: Improve this function (inet_pton behaves strangely with ipv6). + * for now the only guarantee is that it won't return true if the + * address is a domain or ipv4 address, and should only be used if you're + * reasonably sure that the address is one of the three (ipv4, ipv6 or a domain). + */ +bool is_ip6_address(const char *address); + #endif /* #define MISC_TOOLS_H */ From 38ec96e96a194f783d11000d2f5fb5e4a5a87ff1 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Thu, 22 Sep 2016 14:09:07 -0400 Subject: [PATCH 20/23] Thread nodeslist loading This is necessary because DNS/http lookups block, and can do so for a very long time --- src/bootstrap.c | 137 ++++++++++++++++++++++++++++++++++++---------- src/bootstrap.h | 13 +++-- src/name_lookup.c | 6 +- src/toxic.c | 13 ++--- 4 files changed, 124 insertions(+), 45 deletions(-) diff --git a/src/bootstrap.c b/src/bootstrap.c index 51fd760..5c61bb0 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -37,6 +37,7 @@ extern struct arg_opts arg_opts; extern struct user_settings *user_settings; +extern struct Winthread Winthread; /* URL that we get the JSON encoded nodes list from. */ #define NODES_LIST_URL "https://nodes.tox.chat/json" @@ -77,7 +78,15 @@ extern struct user_settings *user_settings; /* Maximum allowable size of the nodes list */ #define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE) -#define MAXNODES 50 + +struct Thread_Data { + pthread_t tid; + pthread_attr_t attr; + pthread_mutex_t lock; + volatile bool active; +} thread_data; + +#define MAX_NODES 50 struct Node { char ip4[IP_MAX_SIZE + 1]; bool have_ip4; @@ -90,7 +99,7 @@ struct Node { }; static struct DHT_Nodes { - struct Node list[MAXNODES]; + struct Node list[MAX_NODES]; size_t count; uint64_t last_updated; } Nodes; @@ -101,7 +110,6 @@ static struct DHT_Nodes { */ #define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping)) - /* Return true if nodeslist pointed to by fp needs to be updated. * This will be the case if the file is empty, has an invalid format, * or if the file is older than the given timeout. @@ -134,11 +142,17 @@ static bool nodeslist_needs_update(const char *nodes_path) return true; } - last_scan_val += LAST_SCAN_JSON_KEY_LEN; - long long int last_scan = strtoll(last_scan_val, NULL, 10); - Nodes.last_updated = last_scan; + long long int last_scan = strtoll(last_scan_val + LAST_SCAN_JSON_KEY_LEN, NULL, 10); - if (timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60)) { + pthread_mutex_lock(&thread_data.lock); + Nodes.last_updated = last_scan; + pthread_mutex_unlock(&thread_data.lock); + + pthread_mutex_lock(&Winthread.lock); + bool is_timeout = timed_out(last_scan, user_settings->nodeslist_update_freq * 24 * 60 * 60); + pthread_mutex_unlock(&Winthread.lock); + + if (is_timeout) { return true; } @@ -158,6 +172,8 @@ static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data) return -1; } + int err = -1; + struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, "charsets: utf-8"); @@ -173,21 +189,21 @@ static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data) if (proxy_ret != 0) { fprintf(stderr, "set_curl_proxy() failed with error %d\n", proxy_ret); - return -1; + goto on_exit; } int ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); if (ret != CURLE_OK) { fprintf(stderr, "TLSv1.2 could not be set (libcurl error %d)", ret); - return -1; + goto on_exit; } ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST); if (ret != CURLE_OK) { fprintf(stderr, "Failed to set TLS cipher list (libcurl error %d)", ret); - return -1; + goto on_exit; } ret = curl_easy_perform(c_handle); @@ -201,11 +217,16 @@ static int curl_fetch_nodes_JSON(struct Recv_Curl_Data *recv_data) if (ret != CURLE_OK) { fprintf(stderr, "HTTPS lookup error (libcurl error %d)\n", ret); - return -1; + goto on_exit; } } - return 0; + err = 0; + +on_exit: + curl_slist_free_all(headers); + curl_easy_cleanup(c_handle); + return err; } /* Attempts to update the DHT nodeslist. @@ -401,15 +422,8 @@ static int extract_node(const char *line, struct Node *node) return 0; } -/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. - * TODO: Parse json using a proper library? - * - * Return 0 on success. - * Return -1 if nodeslist file cannot be opened or created. - * Return -2 if nodeslist file cannot be parsed. - * Return -3 if nodeslist file does not contain any valid node entries. - */ -int load_DHT_nodeslist(void) +/* Loads the DHT nodeslist to memory from json encoded nodes file. */ +void *load_nodeslist_thread(void *data) { char nodes_path[PATH_MAX]; get_nodeslist_path(nodes_path, sizeof(nodes_path)); @@ -418,10 +432,12 @@ int load_DHT_nodeslist(void) if (!file_exists(nodes_path)) { if ((fp = fopen(nodes_path, "w+")) == NULL) { - return -1; + fprintf(stderr, "nodeslist load error: failed to create file '%s'\n", nodes_path); + goto on_exit; } } else if ((fp = fopen(nodes_path, "r+")) == NULL) { - return -1; + fprintf(stderr, "nodeslist load error: failed to open file '%s'\n", nodes_path); + goto on_exit; } int update_err = update_DHT_nodeslist(nodes_path); @@ -434,45 +450,103 @@ int load_DHT_nodeslist(void) if (fgets(line, sizeof(line), fp) == NULL) { fclose(fp); - return -2; + fprintf(stderr, "nodeslist load error: file empty.\n"); + goto on_exit; } + size_t idx = 0; const char *line_start = line; - while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY)) && Nodes.count < MAXNODES) { - size_t idx = Nodes.count; + while ((line_start = strstr(line_start + 1, IPV4_JSON_KEY))) { + pthread_mutex_lock(&thread_data.lock); + idx = Nodes.count; + + if (idx >= MAX_NODES) { + pthread_mutex_unlock(&thread_data.lock); + break; + } if (extract_node(line_start, &Nodes.list[idx]) == 0) { ++Nodes.count; } + + pthread_mutex_unlock(&thread_data.lock); } /* If nodeslist does not contain any valid entries we set the last_scan value * to 0 so that it will fetch a new list the next time this function is called. */ - if (Nodes.count == 0) { + if (idx == 0) { const char *s = "{\"last_scan\":0}"; rewind(fp); fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails fclose(fp); - return -3; + fprintf(stderr, "nodeslist load error: List did not contain any valid entries.\n"); + goto on_exit; } fclose(fp); + +on_exit: + thread_data.active = false; + pthread_attr_destroy(&thread_data.attr); + pthread_exit(0); +} + +/* Creates a new thread that will load the DHT nodeslist to memory + * from json encoded nodes file obtained at NODES_LIST_URL. Only one + * thread may run at a time. + * + * Return 0 on success. + * Return -1 if a thread is already active. + * Return -2 if mutex fails to init. + * Return -3 if pthread attribute fails to init. + * Return -4 if pthread fails to set detached state. + * Return -5 if thread creation fails. + */ +int load_DHT_nodeslist(void) +{ + if (thread_data.active) { + return -1; + } + + if (pthread_mutex_init(&thread_data.lock, NULL) != 0) { + return -2; + } + + if (pthread_attr_init(&thread_data.attr) != 0) { + return -3; + } + + if (pthread_attr_setdetachstate(&thread_data.attr, PTHREAD_CREATE_DETACHED) != 0) { + return -4; + } + + thread_data.active = true; + if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) { + thread_data.active = false; + return -5; + } + return 0; } /* Connects to NUM_BOOTSTRAP_NODES random DHT nodes listed in the DHTnodes file. */ static void DHT_bootstrap(Tox *m) { - if (Nodes.count == 0) { + pthread_mutex_lock(&thread_data.lock); + size_t num_nodes = Nodes.count; + pthread_mutex_unlock(&thread_data.lock); + + if (num_nodes == 0) { return; } size_t i; + pthread_mutex_lock(&thread_data.lock); + for (i = 0; i < NUM_BOOTSTRAP_NODES; ++i) { - TOX_ERR_BOOTSTRAP err; struct Node *node = &Nodes.list[rand() % Nodes.count]; const char *addr = node->have_ip4 ? node->ip4 : node->ip6; @@ -480,6 +554,7 @@ static void DHT_bootstrap(Tox *m) continue; } + TOX_ERR_BOOTSTRAP err; tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); if (err != TOX_ERR_BOOTSTRAP_OK) { @@ -492,6 +567,8 @@ static void DHT_bootstrap(Tox *m) fprintf(stderr, "Failed to add TCP relay %s:%d\n", addr, node->port); } } + + pthread_mutex_unlock(&thread_data.lock); } /* Manages connection to the Tox DHT network. */ diff --git a/src/bootstrap.h b/src/bootstrap.h index b711a24..3a07cdf 100644 --- a/src/bootstrap.h +++ b/src/bootstrap.h @@ -26,13 +26,16 @@ /* Manages connection to the Tox DHT network. */ void do_tox_connection(Tox *m); -/* Load the DHT nodeslist to memory from json encoded nodes file obtained at NODES_LIST_URL. - * TODO: Parse json using a proper library? +/* Creates a new thread that will load the DHT nodeslist to memory + * from json encoded nodes file obtained at NODES_LIST_URL. Only one + * thread may run at a time. * * Return 0 on success. - * Return -1 if nodeslist file cannot be opened or created. - * Return -2 if nodeslist file cannot be parsed. - * Return -3 if nodeslist file does not contain any valid node entries. + * Return -1 if a thread is already active. + * Return -2 if mutex fails to init. + * Return -3 if pthread attribute fails to init. + * Return -4 if pthread fails to set detached state. + * Return -5 if thread creation fails. */ int load_DHT_nodeslist(void); diff --git a/src/name_lookup.c b/src/name_lookup.c index b78d3f4..4b88068 100644 --- a/src/name_lookup.c +++ b/src/name_lookup.c @@ -33,7 +33,7 @@ #include "curl_util.h" extern struct arg_opts arg_opts; -extern struct Winthread Winthread;; +extern struct Winthread Winthread; #define NAMESERVER_API_PATH "api" #define SERVER_KEY_SIZE 32 @@ -53,8 +53,8 @@ static struct thread_data { char id_bin[TOX_ADDRESS_SIZE]; char addr[MAX_STR_SIZE]; char msg[MAX_STR_SIZE]; - bool busy; bool disabled; + volatile bool busy; } t_data; static struct lookup_thread { @@ -234,7 +234,7 @@ void *lookup_thread_func(void *data) if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { if (!strcasecmp(input_domain, "utox.org")) - lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic"); + lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic."); else lookup_error(self, "Name server domain not found."); diff --git a/src/toxic.c b/src/toxic.c index f064eb7..a6e4afc 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -1140,12 +1140,6 @@ int main(int argc, char **argv) queue_init_message("Name lookup server list does not contain any valid entries."); } - int nodeslist_ret = load_DHT_nodeslist(); - - if (nodeslist_ret != 0) { - queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret); - } - #ifdef X11 if (init_xtra(DnD_callback) == -1) queue_init_message("X failed to initialize"); @@ -1173,7 +1167,6 @@ int main(int argc, char **argv) if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); - #ifdef AUDIO av = init_audio(prompt, m); @@ -1202,6 +1195,12 @@ int main(int argc, char **argv) if (init_mplex_away_timer(m) == -1) queue_init_message("Failed to init mplex auto-away."); + int nodeslist_ret = load_DHT_nodeslist(); + + if (nodeslist_ret != 0) { + queue_init_message("DHT nodeslist failed to load (error %d)", nodeslist_ret); + } + pthread_mutex_lock(&Winthread.lock); print_init_messages(prompt); pthread_mutex_unlock(&Winthread.lock); From c24e1bd2b84c7a5e78963b135b4b4fec4f9b9e61 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Thu, 22 Sep 2016 18:00:14 -0400 Subject: [PATCH 21/23] Fix a few A/V race conditions --- src/audio_device.c | 22 +++++++++++----------- src/bootstrap.c | 2 +- src/groupchat.c | 9 +++++++++ src/video_device.c | 22 ++++++++++++++-------- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/audio_device.c b/src/audio_device.c index 9e6c1e2..6ff8f80 100644 --- a/src/audio_device.c +++ b/src/audio_device.c @@ -321,9 +321,6 @@ DeviceError close_device(DeviceType type, uint32_t device_idx) running[type][device_idx] = NULL; if ( !device->ref_count ) { - -// printf("Closed device "); - if (type == input) { if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; } @@ -414,23 +411,26 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source int32_t sample = 0; - while (true) + while (1) { lock; if (!thread_running) { unlock; break; } + + bool paused = thread_paused; unlock; - if (thread_paused) usleep(10000); /* Wait for unpause. */ - else - { - for (i = 0; i < size[input]; ++i) - { + /* Wait for unpause. */ + if (paused) { + usleep(10000); + } + + else { + for (i = 0; i < size[input]; ++i) { lock; - if (running[input][i] != NULL) - { + if (running[input][i] != NULL) { alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample); int f_size = (running[input][i]->sample_rate * running[input][i]->frame_duration / 1000); diff --git a/src/bootstrap.c b/src/bootstrap.c index 5c61bb0..9dec19b 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -79,7 +79,7 @@ extern struct Winthread Winthread; #define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE) -struct Thread_Data { +static struct Thread_Data { pthread_t tid; pthread_attr_t attr; pthread_mutex_t lock; diff --git a/src/groupchat.c b/src/groupchat.c index 0e58cf3..27988c1 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -683,7 +683,9 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m) mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT); mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); + pthread_mutex_lock(&Winthread.lock); int num_peers = groupchats[self->num].num_peers; + pthread_mutex_unlock(&Winthread.lock); wmove(ctx->sidebar, 0, 1); wattron(ctx->sidebar, A_BOLD); @@ -698,12 +700,19 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m) for (i = 0; i < num_peers && i < maxlines; ++i) { wmove(ctx->sidebar, i + 2, 1); + + pthread_mutex_lock(&Winthread.lock); int peer = i + groupchats[self->num].side_pos; + pthread_mutex_unlock(&Winthread.lock); /* truncate nick to fit in side panel without modifying list */ char tmpnck[TOX_MAX_NAME_LENGTH]; int maxlen = SIDEBAR_WIDTH - 2; + + pthread_mutex_lock(&Winthread.lock); memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen); + pthread_mutex_unlock(&Winthread.lock); + tmpnck[maxlen] = '\0'; wprintw(ctx->sidebar, "%s\n", tmpnck); diff --git a/src/video_device.c b/src/video_device.c index 2307049..7f2227e 100644 --- a/src/video_device.c +++ b/src/video_device.c @@ -236,7 +236,10 @@ VideoDeviceError init_video_devices() VideoDeviceError terminate_video_devices() { /* Cleanup if needed */ + lock; video_thread_running = false; + unlock; + usleep(20000); int i; @@ -614,16 +617,19 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so (void)arg; uint32_t i; - while (video_thread_running) - { + while (1) { + lock; + if (!video_thread_running) { + unlock; + break; + } + unlock; + if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */ - else - { - for (i = 0; i < size[vdt_input]; ++i) - { + else { + for (i = 0; i < size[vdt_input]; ++i) { lock; - if ( video_devices_running[vdt_input][i] != NULL ) - { + if ( video_devices_running[vdt_input][i] != NULL ) { /* Obtain frame image data from device buffers */ VideoDevice* device = video_devices_running[vdt_input][i]; uint16_t video_width = device->video_width; From 2194b9e2598d43a7d0479c099ea71328854888ea Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Thu, 22 Sep 2016 18:31:52 -0400 Subject: [PATCH 22/23] Use time_t instead of uint64_t for timestamps Also call time() directly from get_unix_time() instead of manually updating the time val. --- src/bootstrap.c | 4 ++-- src/chat.c | 2 +- src/file_transfers.h | 4 ++-- src/friendlist.c | 8 ++++---- src/groupchat.c | 2 +- src/groupchat.h | 2 +- src/line_info.h | 2 +- src/log.h | 2 +- src/message_queue.h | 2 +- src/misc_tools.c | 18 +++++------------- src/misc_tools.h | 9 +++------ src/toxic.c | 6 ++---- src/windows.h | 4 ++-- 13 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/bootstrap.c b/src/bootstrap.c index 9dec19b..f46f942 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -101,7 +101,7 @@ struct Node { static struct DHT_Nodes { struct Node list[MAX_NODES]; size_t count; - uint64_t last_updated; + time_t last_updated; } Nodes; @@ -574,7 +574,7 @@ static void DHT_bootstrap(Tox *m) /* Manages connection to the Tox DHT network. */ void do_tox_connection(Tox *m) { - static uint64_t last_bootstrap_time = 0; + static time_t last_bootstrap_time = 0; bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE; if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) { diff --git a/src/chat.c b/src/chat.c index b33c475..2438ae4 100644 --- a/src/chat.c +++ b/src/chat.c @@ -825,7 +825,7 @@ static void draw_infobox(ToxWindow *self) if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT) return; - uint64_t curtime = get_unix_time(); + time_t curtime = get_unix_time(); /* update elapsed time string once per second */ if (curtime > infobox->lastupdate) diff --git a/src/file_transfers.h b/src/file_transfers.h index aaf081e..6f58204 100644 --- a/src/file_transfers.h +++ b/src/file_transfers.h @@ -61,8 +61,8 @@ struct FileTransfer { size_t index; uint64_t file_size; uint64_t position; - uint64_t last_line_progress; /* The last time we updated the progress bar */ - uint64_t last_keep_alive; /* The last time we sent or received data */ + time_t last_line_progress; /* The last time we updated the progress bar */ + time_t last_keep_alive; /* The last time we sent or received data */ uint32_t line_id; uint8_t file_id[TOX_FILE_ID_LENGTH]; }; diff --git a/src/friendlist.c b/src/friendlist.c index b159a2d..ae9e98f 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -314,7 +314,7 @@ static void sort_blocklist_index(void) qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block); } -static void update_friend_last_online(uint32_t num, uint64_t timestamp) +static void update_friend_last_online(uint32_t num, time_t timestamp) { Friends.list[num].last_online.last_on = timestamp; Friends.list[num].last_online.tm = *localtime((const time_t*)×tamp); @@ -439,7 +439,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort) fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr); TOX_ERR_FRIEND_GET_LAST_ONLINE loerr; - uint64_t t = tox_friend_get_last_online(m, num, &loerr); + time_t t = tox_friend_get_last_online(m, num, &loerr); if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) t = 0; @@ -910,7 +910,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) return; } - uint64_t cur_time = time(NULL); + time_t cur_time = get_unix_time(); struct tm cur_loc_tm = *localtime((const time_t *) &cur_time); wattron(self->window, A_BOLD); @@ -1047,7 +1047,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wattroff(self->window, COLOR_PAIR(BLUE)); pthread_mutex_lock(&Winthread.lock); - uint64_t last_seen = Friends.list[f].last_online.last_on; + time_t last_seen = Friends.list[f].last_online.last_on; pthread_mutex_unlock(&Winthread.lock); if (last_seen != 0) { diff --git a/src/groupchat.c b/src/groupchat.c index 27988c1..4b12113 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -387,7 +387,7 @@ struct group_add_thrd { ToxWindow *self; int peernum; int groupnum; - uint64_t timestamp; + time_t timestamp; pthread_t tid; pthread_attr_t attr; }; diff --git a/src/groupchat.h b/src/groupchat.h index 59d59c8..cee031c 100644 --- a/src/groupchat.h +++ b/src/groupchat.h @@ -64,7 +64,7 @@ typedef struct { uint8_t type; int num_peers; int side_pos; /* current position of the sidebar - used for scrolling up and down */ - uint64_t start_time; + time_t start_time; uint8_t *peer_names; uint8_t *oldpeer_names; uint16_t *peer_name_lengths; diff --git a/src/line_info.h b/src/line_info.h index 7bd1aa8..9eebaa1 100644 --- a/src/line_info.h +++ b/src/line_info.h @@ -50,7 +50,7 @@ struct line_info { char name1[TOXIC_MAX_NAME_LENGTH + 1]; char name2[TOXIC_MAX_NAME_LENGTH + 1]; char msg[MAX_LINE_INFO_MSG_SIZE]; - uint64_t timestamp; + time_t timestamp; uint8_t type; uint8_t bold; uint8_t colour; diff --git a/src/log.h b/src/log.h index 4bfa708..fb2889c 100644 --- a/src/log.h +++ b/src/log.h @@ -25,7 +25,7 @@ struct chatlog { FILE *file; - uint64_t lastwrite; + time_t lastwrite; char path[MAX_STR_SIZE]; bool log_on; /* specific to current chat window */ }; diff --git a/src/message_queue.h b/src/message_queue.h index df0317e..6b41529 100644 --- a/src/message_queue.h +++ b/src/message_queue.h @@ -29,7 +29,7 @@ struct cqueue_msg { int line_id; uint8_t type; uint32_t receipt; - uint64_t last_send_try; + time_t last_send_try; struct cqueue_msg *next; struct cqueue_msg *prev; }; diff --git a/src/misc_tools.c b/src/misc_tools.c index 8602538..d74875a 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -39,8 +39,6 @@ extern ToxWindow *prompt; extern struct user_settings *user_settings; -static uint64_t current_unix_time; - void hst_to_net(uint8_t *num, uint16_t numbytes) { #ifndef WORDS_BIGENDIAN @@ -56,19 +54,13 @@ void hst_to_net(uint8_t *num, uint16_t numbytes) return; } -/* Note: The time functions are not thread safe */ -void update_unix_time(void) +time_t get_unix_time(void) { - current_unix_time = (uint64_t) time(NULL); -} - -uint64_t get_unix_time(void) -{ - return current_unix_time; + return time(NULL); } /* Returns 1 if connection has timed out, 0 otherwise */ -int timed_out(uint64_t timestamp, uint64_t timeout) +int timed_out(time_t timestamp, time_t timeout) { return timestamp + timeout <= get_unix_time(); } @@ -77,7 +69,7 @@ int timed_out(uint64_t timestamp, uint64_t timeout) struct tm *get_time(void) { struct tm *timeinfo; - uint64_t t = get_unix_time(); + time_t t = get_unix_time(); timeinfo = localtime((const time_t*) &t); return timeinfo; } @@ -95,7 +87,7 @@ void get_time_str(char *buf, int bufsize) } /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ -void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) +void get_elapsed_time_str(char *buf, int bufsize, time_t secs) { if (!secs) return; diff --git a/src/misc_tools.h b/src/misc_tools.h index ef450c9..8a5e36d 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -61,20 +61,17 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr); int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size); /* get the current unix time (not thread safe) */ -uint64_t get_unix_time(void); +time_t get_unix_time(void); /* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ void get_time_str(char *buf, int bufsize); /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ -void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs); +void get_elapsed_time_str(char *buf, int bufsize, time_t secs); /* get the current local time (not thread safe) */ struct tm *get_time(void); -/* updates current unix time (should be run once per do_toxic loop) */ -void update_unix_time(void); - /* Returns 1 if the string is empty, 0 otherwise */ int string_is_empty(const char *string); @@ -91,7 +88,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n); int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); /* Returns 1 if connection has timed out, 0 otherwise */ -int timed_out(uint64_t timestamp, uint64_t timeout); +int timed_out(time_t timestamp, time_t timeout); /* Colours the window tab according to type. Beeps if is_beep is true */ void alert_window(ToxWindow *self, int type, bool is_beep); diff --git a/src/toxic.c b/src/toxic.c index a6e4afc..ba3aee4 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -733,7 +733,6 @@ static Tox *load_toxic(char *data_path) static void do_toxic(Tox *m) { pthread_mutex_lock(&Winthread.lock); - update_unix_time(); if (arg_opts.no_connect) { pthread_mutex_unlock(&Winthread.lock); @@ -1091,7 +1090,6 @@ void DnD_callback(const char* asdv, DropType dt) int main(int argc, char **argv) { - update_unix_time(); parse_args(argc, argv); /* Use the -b flag to enable stderr */ @@ -1212,12 +1210,12 @@ int main(int argc, char **argv) snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); - uint64_t last_save = (uint64_t) time(NULL); + time_t last_save = get_unix_time(); while (true) { do_toxic(m); - uint64_t cur_time = get_unix_time(); + time_t cur_time = get_unix_time(); if (timed_out(last_save, AUTOSAVE_FREQ)) { pthread_mutex_lock(&Winthread.lock); diff --git a/src/windows.h b/src/windows.h index 21f1386..4484eaf 100644 --- a/src/windows.h +++ b/src/windows.h @@ -204,8 +204,8 @@ struct infobox { bool hide; bool active; - uint64_t lastupdate; - uint64_t starttime; + time_t lastupdate; + time_t starttime; char timestr[TIME_STR_SIZE]; WINDOW *win; From 35156231591286ea9936900ade6d63c9f10e4e54 Mon Sep 17 00:00:00 2001 From: Jfreegman Date: Fri, 23 Sep 2016 13:05:05 -0400 Subject: [PATCH 23/23] Bump version to 0.7.1 --- cfg/global_vars.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/global_vars.mk b/cfg/global_vars.mk index 0503b90..af4bbae 100644 --- a/cfg/global_vars.mk +++ b/cfg/global_vars.mk @@ -1,5 +1,5 @@ # Version -TOXIC_VERSION = 0.7.0 +TOXIC_VERSION = 0.7.1 REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") ifneq (, $(findstring error, $(REV))) VERSION = $(TOXIC_VERSION)