diff --git a/README.md b/README.md index e09e30d..b8ead14 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,9 @@ Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly [![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png) ## Installation -[Use our repositories](https://wiki.tox.chat/doku.php?id=developers:binaries#other_linux)
+[Use our repositories](https://wiki.tox.chat/binaries#other_linux)
[Compile it yourself](/INSTALL.md) -## Downloads -If you don't like installation methods listed above, you can still download precompiled binaries from [jenkins](https://jenkins.libtoxcore.so): -* [Linux 32 bit](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz) [![Build Status](https://jenkins.libtoxcore.so/job/toxic_linux_i386/badge/icon)](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz) -* [Linux 64 bit](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz) [![Build Status](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/badge/icon)](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz) -* [~~Linux ARMv6~~](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/lastSuccessfulBuild/artifact/toxic_linux_armv6.tar.xz) **CURRENTLY DISABLED** [![Build Status](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/badge/icon)](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/lastSuccessfulBuild/artifact/toxic_linux_armv6.tar.xz) - -#### DEBs packages -* [toxic-i386.deb](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-i386.deb) -* [toxic-x86_64.deb](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-x86_64.deb) - -#### RPMs packages -* [toxic-i386.rpm](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-i386.rpm) -* [toxic-x86_64.rpm](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-x86_64.rpm) - ## Settings Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. You can view our example config file [here](misc/toxic.conf.example). diff --git a/src/autocomplete.c b/src/autocomplete.c index 8cf703c..6bc3c6a 100644 --- a/src/autocomplete.c +++ b/src/autocomplete.c @@ -93,7 +93,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) { ChatContext *ctx = self->chatwin; - if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) + if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) return -1; const char *L = (char *) list; @@ -136,7 +136,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) } } - if (string_is_empty(sub)) { + if (!sub[0]) { free(sub); return -1; } @@ -185,7 +185,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) return -1; char tmpend[MAX_STR_SIZE]; - strcpy(tmpend, &ubuf[ctx->pos]); + snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); strcpy(&ubuf[strt], match); strcpy(&ubuf[strt + m_len], endchrs); strcpy(&ubuf[strt + m_len + n_endchrs], tmpend); @@ -193,7 +193,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) /* convert to widechar and copy back to original buf */ wchar_t newbuf[MAX_STR_SIZE]; - if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1) + if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1) return -1; wcscpy(ctx->line, newbuf); @@ -218,7 +218,7 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha wchar_t wline[MAX_STR_SIZE]; - if (mbs_to_wcs_buf(wline, newline, sizeof(wline)) == -1) + if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) return; int newlen = wcslen(wline); @@ -261,10 +261,10 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) } else if (!si && b_path[0] != '/') { /* look for matches in pwd */ char tmp[MAX_STR_SIZE]; snprintf(tmp, sizeof(tmp), ".%s", b_path); - strcpy(b_path, tmp); + snprintf(b_path, sizeof(b_path), "%s", tmp); } - strcpy(b_name, &b_path[si + 1]); + snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]); b_path[si + 1] = '\0'; int b_name_len = strlen(b_name); DIR *dp = opendir(b_path); diff --git a/src/avatars.c b/src/avatars.c index a17c3be..d58b27e 100644 --- a/src/avatars.c +++ b/src/avatars.c @@ -63,7 +63,7 @@ int avatar_send(Tox *m, uint32_t friendnum) return -1; } - struct FileTransfer *ft = get_new_file_sender(friendnum); + struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); if (!ft) return -1; @@ -75,11 +75,6 @@ int avatar_send(Tox *m, uint32_t friendnum) snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); ft->file_size = Avatar.size; - ft->state = FILE_TRANSFER_PENDING; - ft->filenum = filenum; - ft->friendnum = friendnum; - ft->direction = FILE_TRANSFER_SEND; - ft->file_type = TOX_FILE_KIND_AVATAR; return 0; } @@ -206,4 +201,5 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); ft->position += send_length; + ft->last_keep_alive = get_unix_time(); } diff --git a/src/chat.c b/src/chat.c index 9d6e8a0..7d17d0d 100644 --- a/src/chat.c +++ b/src/chat.c @@ -189,8 +189,8 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TY return recv_action_helper(self, m, num, msg, len, nick, timefrmt); } -static void chat_resume_file_transfers(Tox *m, uint32_t fnum); -static void chat_stop_file_senders(Tox *m, uint32_t friendnum); +static void chat_pause_file_transfers(Tox *m, uint32_t friendnum); +static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t fnum); static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status) { @@ -210,7 +210,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C if (connection_status != TOX_CONNECTION_NONE && statusbar->connection == TOX_CONNECTION_NONE) { Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON ? tox_friend_get_typing(m, num, NULL) : false; - chat_resume_file_transfers(m, num); + chat_resume_file_senders(self, m, num); msg = "has come online"; line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); @@ -221,7 +221,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C if (self->chatwin->self_is_typing) set_self_typingstatus(self, m, 0); - chat_stop_file_senders(m, num); + chat_pause_file_transfers(m, num); msg = "has gone offline"; line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); @@ -278,17 +278,44 @@ static void chat_onReadReceipt(ToxWindow *self, Tox *m, uint32_t num, uint32_t r cqueue_remove(self, m, receipt); } -/* Stops active file senders for this friend. Call when a friend goes offline */ -static void chat_stop_file_senders(Tox *m, uint32_t friendnum) +/* Stops active file transfers for this friend. Called when a friend goes offline */ +static void chat_pause_file_transfers(Tox *m, uint32_t friendnum) { - // TODO: core purges file transfers when a friend goes offline. Ideally we want to repair/resume - kill_all_file_transfers_friend(m, friendnum); + ToxicFriend *friend = &Friends.list[friendnum]; + + size_t i; + + for (i = 0; i < MAX_FILES; ++i) { + if (friend->file_sender[i].state >= FILE_TRANSFER_STARTED) + friend->file_sender[i].state = FILE_TRANSFER_PAUSED; + + if (friend->file_receiver[i].state >= FILE_TRANSFER_STARTED) + friend->file_receiver[i].state = FILE_TRANSFER_PAUSED; + } } -/* Tries to resume broken file transfers. Call when a friend comes online */ -static void chat_resume_file_transfers(Tox *m, uint32_t fnum) +/* Tries to resume broken file senders. Called when a friend comes online */ +static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t friendnum) { - // TODO + size_t i; + + for (i = 0; i < MAX_FILES; ++i) { + struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; + + if (ft->state != FILE_TRANSFER_PAUSED) + continue; + + TOX_ERR_FILE_SEND err; + ft->filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, ft->file_size, ft->file_id, + (uint8_t *) ft->file_name, strlen(ft->file_name), &err); + + if (err != TOX_ERR_FILE_SEND_OK) { + char msg[MAX_STR_SIZE]; + snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", ft->file_name); + close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); + continue; + } + } } static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position, @@ -347,6 +374,7 @@ static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum, ft->position += send_length; ft->bps += send_length; + ft->last_keep_alive = get_unix_time(); } static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position, @@ -386,6 +414,7 @@ static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t friendnum, ui ft->bps += length; ft->position += length; + ft->last_keep_alive = get_unix_time(); } static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, TOX_FILE_CONTROL control) @@ -401,14 +430,16 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint char msg[MAX_STR_SIZE]; switch (control) { - case TOX_FILE_CONTROL_RESUME: + case TOX_FILE_CONTROL_RESUME: { + ft->last_keep_alive = get_unix_time(); + /* transfer is accepted */ if (ft->state == FILE_TRANSFER_PENDING) { ft->state = FILE_TRANSFER_STARTED; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.", ft->index, ft->file_name); char progline[MAX_STR_SIZE]; - prep_prog_line(progline); + 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); ft->line_id = self->chatwin->hst->line_end->id + 2; @@ -417,25 +448,78 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint } break; - - case TOX_FILE_CONTROL_PAUSE: + } + case TOX_FILE_CONTROL_PAUSE: { ft->state = FILE_TRANSFER_PAUSED; break; - - case TOX_FILE_CONTROL_CANCEL: + } + case TOX_FILE_CONTROL_CANCEL: { snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name); close_file_transfer(self, m, ft, -1, msg, notif_error); break; + } } } +/* Attempts to resume a broken inbound file transfer. + * + * Returns true if resume is successful. + */ +static bool chat_resume_broken_ft(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum) +{ + char msg[MAX_STR_SIZE]; + uint8_t file_id[TOX_FILE_ID_LENGTH]; + + if (!tox_file_get_file_id(m, friendnum, filenum, file_id, NULL)) + return false; + + bool resuming = false; + struct FileTransfer *ft = NULL; + size_t i; + + for (i = 0; i < MAX_FILES; ++i) { + ft = &Friends.list[friendnum].file_receiver[i]; + + if (ft->state == FILE_TRANSFER_INACTIVE) + continue; + + if (memcmp(ft->file_id, file_id, TOX_FILE_ID_LENGTH) == 0) { + ft->filenum = filenum; + ft->state = FILE_TRANSFER_STARTED; + ft->last_keep_alive = get_unix_time(); + resuming = true; + break; + } + } + + if (!resuming || !ft) + return false; + + if (!tox_file_seek(m, ft->friendnum, ft->filenum, ft->position, NULL)) + goto on_error; + + if (!tox_file_control(m, ft->friendnum, ft->filenum, TOX_FILE_CONTROL_RESUME, NULL)) + goto on_error; + + return true; + +on_error: + snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", ft->file_name); + close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); + return false; +} + static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t file_size, const char *filename, size_t name_length) { if (self->num != friendnum) return; - struct FileTransfer *ft = get_new_file_receiver(friendnum); + /* first check if we need to resume a broken transfer */ + if (chat_resume_broken_ft(self, m, friendnum, filenum)) + return; + + struct FileTransfer *ft = new_file_transfer(self, friendnum, filenum, FILE_TRANSFER_RECV, TOX_FILE_KIND_DATA); if (!ft) { tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL); @@ -458,7 +542,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ snprintf(file_path, sizeof(file_path), "%s", filename); } - if (path_len >= sizeof(ft->file_path) || name_length >= sizeof(ft->file_name)) { + if (path_len >= sizeof(file_path) || path_len >= sizeof(ft->file_path) || name_length >= sizeof(ft->file_name)) { tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer faield: File path too long."); return; @@ -493,14 +577,10 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_ line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", ft->index); - ft->state = FILE_TRANSFER_PENDING; - ft->direction = FILE_TRANSFER_RECV; ft->file_size = file_size; - ft->friendnum = friendnum; - ft->filenum = filenum; - ft->file_type = TOX_FILE_KIND_DATA; snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path); snprintf(ft->file_name, sizeof(ft->file_name), "%s", filename); + 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, @@ -851,7 +931,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (diff != -1) { if (x + diff > x2 - 1) { - int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { @@ -1005,7 +1085,7 @@ static void chat_onDraw(ToxWindow *self, Tox *m) int y, x; getyx(self->window, y, x); (void) x; - int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); + int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); wmove(self->window, y + 1, new_x); wrefresh(self->window); @@ -1072,11 +1152,13 @@ static void chat_onInit(ToxWindow *self, Tox *m) char myid[TOX_ADDRESS_SIZE]; tox_self_get_address(m, (uint8_t *) myid); - log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT); + int log_ret = log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT); load_chat_history(self, ctx->log); if (!Friends.list[self->num].logging_on) log_disable(ctx->log); + else if (log_ret == -1) + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); diff --git a/src/chat_commands.c b/src/chat_commands.c index 53fb6ce..de8cb96 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -183,7 +183,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv /* prep progress bar line */ char progline[MAX_STR_SIZE]; - prep_prog_line(progline); + init_progress_bar(progline); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); ft->line_id = self->chatwin->hst->line_end->id + 2; @@ -265,7 +265,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv if (err != TOX_ERR_FILE_SEND_OK) goto on_send_error; - struct FileTransfer *ft = get_new_file_sender(self->num); + struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); if (!ft) { err = TOX_ERR_FILE_SEND_TOO_MANY; @@ -273,13 +273,9 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv } memcpy(ft->file_name, file_name, namelen + 1); - ft->state = FILE_TRANSFER_PENDING; ft->file = file_to_send; ft->file_size = filesize; - ft->filenum = filenum; - ft->friendnum = self->num; - ft->direction = FILE_TRANSFER_SEND; - ft->file_type = TOX_FILE_KIND_DATA; + tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL); char sizestr[32]; bytes_convert_str(sizestr, sizeof(sizestr), filesize); diff --git a/src/device.c b/src/device.c index 4a6a8a5..9ef26de 100644 --- a/src/device.c +++ b/src/device.c @@ -58,14 +58,14 @@ typedef struct Device { DataHandleCallback cb; /* Use this to handle data from input device usually */ void* cb_data; /* Data to be passed to callback */ int32_t call_idx; /* ToxAv call index */ - + uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */ uint32_t ref_count; int32_t selection; bool enable_VAD; bool muted; pthread_mutex_t mutex[1]; - uint32_t sample_rate; + uint32_t sample_rate; uint32_t frame_duration; int32_t sound_mode; #ifdef AUDIO @@ -89,7 +89,7 @@ static ToxAv* av = NULL; pthread_mutex_t mutex; -bool thread_running = true, +bool thread_running = true, thread_paused = true; /* Thread control */ void* thread_poll(void*); @@ -105,19 +105,19 @@ DeviceError init_devices() size[input] = 0; if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) { ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); - + for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) { - devices_names[input][size[input]] = stringed_device_list; + devices_names[input][size[input]] = stringed_device_list; stringed_device_list += strlen( stringed_device_list ) + 1; } } - + size[output] = 0; if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) { ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); - + for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { - devices_names[output][size[output]] = stringed_device_list; + devices_names[output][size[output]] = stringed_device_list; stringed_device_list += strlen( stringed_device_list ) + 1; } } @@ -125,27 +125,30 @@ DeviceError init_devices() // Start poll thread if (pthread_mutex_init(&mutex, NULL) != 0) return de_InternalError; - + pthread_t thread_id; - if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) - return de_InternalError; - + if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) + return de_InternalError; + #ifdef AUDIO av = av_; #endif /* AUDIO */ - + return (DeviceError) de_None; } DeviceError terminate_devices() { /* Cleanup if needed */ + lock; thread_running = false; + unlock; + usleep(20000); - + if (pthread_mutex_destroy(&mutex) != 0) return (DeviceError) de_InternalError; - + return (DeviceError) de_None; } @@ -153,16 +156,16 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx) { if (device_idx >= MAX_DEVICES) return de_InvalidSelection; lock; - + Device* device = running[type][device_idx]; - - if (!device) { + + if (!device) { unlock; return de_DeviceNotActive; } - + device->muted = !device->muted; - + unlock; return de_None; } @@ -175,7 +178,7 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) Device* device = running[input][device_idx]; - if (!device) { + if (!device) { unlock; return de_DeviceNotActive; } @@ -192,7 +195,7 @@ DeviceError set_primary_device(DeviceType type, int32_t selection) { if (size[type] <= selection || selection < 0) return de_InvalidSelection; primary_device[type] = selection; - + return de_None; } @@ -212,88 +215,88 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx if (size[type] <= selection || selection < 0) return de_InvalidSelection; if (channels != 1 && channels != 2) return de_UnsupportedMode; - + lock; const uint32_t frame_size = (sample_rate * frame_duration / 1000); - + uint32_t i; for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i); - + if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; } else *device_idx = i; - + for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ if ( running[type][i] && running[type][i]->selection == selection ) { // printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl); - - running[type][*device_idx] = running[type][i]; + + running[type][*device_idx] = running[type][i]; running[type][i]->ref_count ++; - + unlock; return de_None; } } - + Device* device = running[type][*device_idx] = calloc(1, sizeof(Device)); device->selection = selection; - + device->sample_rate = sample_rate; device->frame_duration = frame_duration; device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; - + if (pthread_mutex_init(device->mutex, NULL) != 0) { free(device); unlock; return de_InternalError; } - + if (type == input) { - device->dhndl = alcCaptureOpenDevice(devices_names[type][selection], + device->dhndl = alcCaptureOpenDevice(devices_names[type][selection], sample_rate, device->sound_mode, frame_size * 2); #ifdef AUDIO device->VAD_treshold = user_settings->VAD_treshold; #endif } - else { + else { device->dhndl = alcOpenDevice(devices_names[type][selection]); - if ( !device->dhndl ) { + if ( !device->dhndl ) { free(device); running[type][*device_idx] = NULL; unlock; return de_FailedStart; } - + device->ctx = alcCreateContext(device->dhndl, NULL); alcMakeContextCurrent(device->ctx); - + alGenBuffers(OPENAL_BUFS, device->buffers); alGenSources((uint32_t)1, &device->source); alSourcei(device->source, AL_LOOPING, AL_FALSE); - + uint16_t zeros[frame_size]; memset(zeros, 0, frame_size*2); - + for ( i = 0; i < OPENAL_BUFS; ++i ) { alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate); } - + alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); alSourcePlay(device->source); } - + if (alcGetError(device->dhndl) != AL_NO_ERROR) { free(device); running[type][*device_idx] = NULL; unlock; return de_FailedStart; } - + if (type == input) { alcCaptureStart(device->dhndl); thread_paused = false; } - + unlock; return de_None; } @@ -301,47 +304,47 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx DeviceError close_device(DeviceType type, uint32_t device_idx) { if (device_idx >= MAX_DEVICES) return de_InvalidSelection; - + lock; Device* device = running[type][device_idx]; DeviceError rc = de_None; - - if (!device) { + + if (!device) { unlock; return de_DeviceNotActive; } - + running[type][device_idx] = NULL; - + if ( !device->ref_count ) { - + // printf("Closed device "); - + if (type == input) { if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; } - else { + else { if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx); - + alDeleteSources(1, &device->source); alDeleteBuffers(OPENAL_BUFS, device->buffers); - + alcMakeContextCurrent(NULL); if ( device->ctx ) alcDestroyContext(device->ctx); if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; } - + free(device); } else device->ref_count--; - + unlock; return rc; } DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD) -{ - if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) +{ + if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) return de_InvalidSelection; lock; @@ -375,9 +378,9 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_ alSourceUnqueueBuffers(device->source, processed, bufids); alDeleteBuffers(processed - 1, bufids + 1); bufid = bufids[0]; - } + } else if(queued < 16) alGenBuffers(1, &bufid); - else { + else { pthread_mutex_unlock(device->mutex); return de_Busy; } @@ -404,44 +407,51 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source (void)arg; uint32_t i; int32_t sample = 0; - - - while (thread_running) + + + while (true) { + lock; + if (!thread_running) { + unlock; + break; + } + unlock; + if (thread_paused) usleep(10000); /* Wait for unpause. */ else { - for (i = 0; i < size[input]; ++i) + 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); - - if (sample < f_size) { + + if (sample < f_size) { unlock; continue; } Device* device = running[input][i]; - + int16_t frame[16000]; alcCaptureSamples(device->dhndl, frame, f_size); - - if (device->muted) { + + if (device->muted) { unlock; continue; } - + if ( device->cb ) device->cb(frame, f_size, device->cb_data); - } + } unlock; } usleep(5000); } } - + pthread_exit(NULL); } @@ -462,8 +472,8 @@ DeviceError selection_valid(DeviceType type, int32_t selection) void* get_device_callback_data(uint32_t device_idx) { - if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) + if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) return NULL; - + return running[input][device_idx]->cb_data; } diff --git a/src/dns.c b/src/dns.c index ade723c..0403f6c 100644 --- a/src/dns.c +++ b/src/dns.c @@ -234,12 +234,16 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char and the domain in dombuf. return length of username on success, -1 on failure */ -static int parse_addr(const char *addr, char *namebuf, char *dombuf) +static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz) { - char tmpaddr[MAX_STR_SIZE]; - char *tmpname, *tmpdom; + if (strlen(addr) >= MAX_STR_SIZE) + return -1; - strcpy(tmpaddr, addr); + char tmpaddr[MAX_STR_SIZE]; + char *tmpname = NULL; + char *tmpdom = NULL; + + snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr); tmpname = strtok(tmpaddr, "@"); tmpdom = strtok(NULL, ""); @@ -247,8 +251,8 @@ static int parse_addr(const char *addr, char *namebuf, char *dombuf) return -1; str_to_lower(tmpdom); - strcpy(namebuf, tmpname); - strcpy(dombuf, tmpdom); + snprintf(namebuf, namebuf_sz, "%s", tmpname); + snprintf(dombuf, dombuf_sz, "%s", tmpdom); return strlen(namebuf); } @@ -295,7 +299,7 @@ void *dns3_lookup_thread(void *data) char inputdomain[MAX_STR_SIZE]; char name[MAX_STR_SIZE]; - int namelen = parse_addr(t_data.addr, name, inputdomain); + int namelen = parse_addr(t_data.addr, name, sizeof(name), inputdomain, sizeof(inputdomain)); if (namelen == -1) { dns_error(self, "Must be a Tox ID or an address in the form username@domain"); diff --git a/src/file_transfers.c b/src/file_transfers.c index 771f958..64261cc 100644 --- a/src/file_transfers.c +++ b/src/file_transfers.c @@ -35,11 +35,52 @@ extern FriendsList Friends; -#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ +/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */ +#define NUM_PROG_MARKS 50 + +/* Checks for timed out file transfers and closes them. */ +#define CHECK_FILE_TIMEOUT_INTERAVAL 5 +void check_file_transfer_timeouts(Tox *m) +{ + char msg[MAX_STR_SIZE]; + static uint64_t last_check = 0; + + if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL)) + return; + + last_check = get_unix_time(); + + size_t i, j; + + for (i = 0; i < Friends.max_idx; ++i) { + if (!Friends.list[i].active) + continue; + + for (j = 0; j < MAX_FILES; ++j) { + struct FileTransfer *ft_send = &Friends.list[i].file_sender[j]; + + if (ft_send->state > FILE_TRANSFER_PAUSED) { + if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) { + snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name); + close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error); + } + } + + struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j]; + + if (ft_recv->state > FILE_TRANSFER_PAUSED) { + if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) { + snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name); + close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error); + } + } + } + } +} /* creates initial progress line that will be updated during file transfer. Assumes progline is of size MAX_STR_SIZE */ -void prep_prog_line(char *progline) +void init_progress_bar(char *progline) { strcpy(progline, "0.0 B/s ["); int i; @@ -80,13 +121,13 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l line_info_set(self, line_id, msg); } -static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft, uint64_t curtime) +static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) { if (ft->state == FILE_TRANSFER_INACTIVE) return; /* Timeout must be set to 1 second to show correct bytes per second */ - if (!timed_out(ft->last_progress, curtime, 1)) + if (!timed_out(ft->last_line_progress, 1)) return; double remain = ft->file_size - ft->position; @@ -94,18 +135,17 @@ static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft, ui print_progress_bar(self, ft->bps, pct_done, ft->line_id); ft->bps = 0; - ft->last_progress = curtime; + ft->last_line_progress = get_unix_time(); } -/* refreshes active file receiver status bars for friendnum */ +/* refreshes active file transfer status bars. */ void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum) { - uint64_t curtime = get_unix_time(); size_t i; for (i = 0; i < MAX_FILES; ++i) { - refresh_progress_helper(self, &Friends.list[friendnum].file_receiver[i], curtime); - refresh_progress_helper(self, &Friends.list[friendnum].file_sender[i], curtime); + refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]); + refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]); } } @@ -157,7 +197,7 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t /* Returns a pointer to an unused file sender. * Returns NULL if all file senders are in use. */ -struct FileTransfer *get_new_file_sender(uint32_t friendnum) +static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) { size_t i; @@ -165,7 +205,15 @@ struct FileTransfer *get_new_file_sender(uint32_t friendnum) struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i]; if (ft->state == FILE_TRANSFER_INACTIVE) { + memset(ft, 0, sizeof(struct FileTransfer)); + ft->window = window; ft->index = i; + ft->friendnum = friendnum; + ft->filenum = filenum; + ft->file_type = type; + ft->last_keep_alive = get_unix_time(); + ft->state = FILE_TRANSFER_PENDING; + ft->direction = FILE_TRANSFER_SEND; return ft; } } @@ -176,7 +224,7 @@ struct FileTransfer *get_new_file_sender(uint32_t friendnum) /* Returns a pointer to an unused file receiver. * Returns NULL if all file receivers are in use. */ -struct FileTransfer *get_new_file_receiver(uint32_t friendnum) +static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) { size_t i; @@ -184,7 +232,15 @@ struct FileTransfer *get_new_file_receiver(uint32_t friendnum) struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i]; if (ft->state == FILE_TRANSFER_INACTIVE) { + memset(ft, 0, sizeof(struct FileTransfer)); + ft->window = window; ft->index = i; + ft->friendnum = friendnum; + ft->filenum = filenum; + ft->file_type = type; + ft->last_keep_alive = get_unix_time(); + ft->state = FILE_TRANSFER_PENDING; + ft->direction = FILE_TRANSFER_RECV; return ft; } } @@ -192,6 +248,22 @@ struct FileTransfer *get_new_file_receiver(uint32_t friendnum) return NULL; } +/* Initializes an unused file transfer and returns its pointer. + * Returns NULL on failure. + */ +struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, + FILE_TRANSFER_DIRECTION direction, uint8_t type) +{ + if (direction == FILE_TRANSFER_RECV) + return new_file_receiver(window, friendnum, filenum, type); + + if (direction == FILE_TRANSFER_SEND) + return new_file_sender(window, friendnum, filenum, type); + + return NULL; +} + + /* Closes file transfer ft. * * Set CTRL to -1 if we don't want to send a control signal. @@ -209,19 +281,19 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C if (ft->file) fclose(ft->file); - memset(ft, 0, sizeof(struct FileTransfer)); - if (CTRL >= 0) tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); if (message && self) { - if (self->active_box != -1) + if (self->active_box != -1 && sound_type != silent) box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); else box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message); } + + memset(ft, 0, sizeof(struct FileTransfer)); } /* Kills all active file transfers for friendnum */ diff --git a/src/file_transfers.h b/src/file_transfers.h index 52b19e1..f5181a6 100644 --- a/src/file_transfers.h +++ b/src/file_transfers.h @@ -30,17 +30,17 @@ #include "notify.h" #define KiB 1024 -#define MiB 1048576 /* 1024 ^ 2 */ -#define GiB 1073741824 /* 1024 ^ 3 */ +#define MiB 1048576 /* 1024^2 */ +#define GiB 1073741824 /* 1024^3 */ #define MAX_FILES 32 #define TIMEOUT_FILESENDER 120 typedef enum FILE_TRANSFER_STATE { FILE_TRANSFER_INACTIVE, + FILE_TRANSFER_PAUSED, FILE_TRANSFER_PENDING, FILE_TRANSFER_STARTED, - FILE_TRANSFER_PAUSED } FILE_TRANSFER_STATE; typedef enum FILE_TRANSFER_DIRECTION { @@ -49,30 +49,36 @@ typedef enum FILE_TRANSFER_DIRECTION { } FILE_TRANSFER_DIRECTION; struct FileTransfer { + ToxWindow *window; FILE *file; FILE_TRANSFER_STATE state; FILE_TRANSFER_DIRECTION direction; uint8_t file_type; char file_name[TOX_MAX_FILENAME_LENGTH + 1]; char file_path[PATH_MAX + 1]; /* Not used by senders */ - double bps; + double bps; uint32_t filenum; uint32_t friendnum; size_t index; uint64_t file_size; uint64_t position; - uint64_t last_progress; + 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 */ uint32_t line_id; + uint8_t file_id[TOX_FILE_ID_LENGTH]; }; +/* Checks for timed out file transfers and closes them. */ +void check_file_transfer_timeouts(Tox *m); + /* creates initial progress line that will be updated during file transfer. progline must be at lesat MAX_STR_SIZE bytes */ -void prep_prog_line(char *progline); +void init_progress_bar(char *progline); /* prints a progress bar for file transfers */ void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); -/* refreshes active file transfer status bars for friendnum */ +/* refreshes active file transfer status bars. */ void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum); /* Returns a pointer to friendnum's FileTransfer struct associated with filenum. @@ -87,15 +93,11 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, FILE_TRANSFER_DIRECTION direction); -/* Returns a pointer to an unused file sender. - * Returns NULL if all file senders are in use. +/* Initializes an unused file transfer and returns its pointer. + * Returns NULL on failure. */ -struct FileTransfer *get_new_file_sender(uint32_t friendnum); - -/* Returns a pointer to an unused file receiver. - * Returns NULL if all file receivers are in use. - */ -struct FileTransfer *get_new_file_receiver(uint32_t friendnum); +struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, + FILE_TRANSFER_DIRECTION direction, uint8_t type); /* Closes file transfer ft. * diff --git a/src/friendlist.c b/src/friendlist.c index e9d8ce6..5ec84fc 100644 --- a/src/friendlist.c +++ b/src/friendlist.c @@ -139,8 +139,10 @@ static int save_blocklist(char *path) int count = 0; for (i = 0; i < Blocked.max_idx; ++i) { - if (count > Blocked.num_blocked) - goto on_error; + if (count > Blocked.num_blocked) { + free(data); + return -1; + } if (Blocked.list[i].active) { BlockedFriend tmp; @@ -161,19 +163,20 @@ static int save_blocklist(char *path) FILE *fp = fopen(path, "wb"); - if (fp == NULL) - goto on_error; + if (fp == NULL) { + free(data); + return -1; + } - if (fwrite(data, len, 1, fp) != 1) - goto on_error; + if (fwrite(data, len, 1, fp) != 1) { + fclose(fp); + free(data); + return -1; + } fclose(fp); free(data); return 0; - -on_error: - free(data); - return -1; } static void sort_blocklist_index(void); @@ -221,9 +224,10 @@ int load_blocklist(char *path) int i; for (i = 0; i < num; ++i) { + BlockedFriend tmp; + memset(&tmp, 0, sizeof(BlockedFriend)); memset(&Blocked.list[i], 0, sizeof(BlockedFriend)); - BlockedFriend tmp; memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend)); Blocked.list[i].active = true; Blocked.list[i].num = i; @@ -616,10 +620,12 @@ static void draw_del_popup(void) wprintw(PendingDelete.popup, "Delete contact "); wattron(PendingDelete.popup, A_BOLD); + pthread_mutex_lock(&Winthread.lock); if (blocklist_view == 0) wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name); else wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name); + pthread_mutex_unlock(&Winthread.lock); wattroff(PendingDelete.popup, A_BOLD); wprintw(PendingDelete.popup, "? y/n"); @@ -880,12 +886,13 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) return; } - uint64_t cur_time = get_unix_time(); - struct tm cur_loc_tm = *localtime((const time_t *) &cur_time); + uint64_t cur_time = time(NULL); + struct tm cur_loc_tm = *localtime((const time_t *) &cur_time); wattron(self->window, A_BOLD); wprintw(self->window, " Online: "); wattroff(self->window, A_BOLD); + wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends); if ((y2 - FLIST_OFST) <= 0) @@ -894,18 +901,30 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) uint32_t selected_num = 0; /* Determine which portion of friendlist to draw based on current position */ + pthread_mutex_lock(&Winthread.lock); int page = Friends.num_selected / (y2 - FLIST_OFST); + pthread_mutex_unlock(&Winthread.lock); + int start = (y2 - FLIST_OFST) * page; int end = y2 - FLIST_OFST + start; + pthread_mutex_lock(&Winthread.lock); + size_t num_friends = Friends.num_friends; + pthread_mutex_unlock(&Winthread.lock); + int i; - for (i = start; i < Friends.num_friends && i < end; ++i) { + for (i = start; i < num_friends && i < end; ++i) { + pthread_mutex_lock(&Winthread.lock); uint32_t f = Friends.index[i]; + bool is_active = Friends.list[f].active; + int num_selected = Friends.num_selected; + pthread_mutex_unlock(&Winthread.lock); + bool f_selected = false; - if (Friends.list[f].active) { - if (i == Friends.num_selected) { + if (is_active) { + if (i == num_selected) { wattron(self->window, A_BOLD); wprintw(self->window, " > "); wattroff(self->window, A_BOLD); @@ -915,8 +934,12 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wprintw(self->window, " "); } - if (Friends.list[f].connection_status != TOX_CONNECTION_NONE) { - TOX_USER_STATUS status = Friends.list[f].status; + pthread_mutex_lock(&Winthread.lock); + TOX_CONNECTION connection_status = Friends.list[f].connection_status; + TOX_USER_STATUS status = Friends.list[f].status; + pthread_mutex_unlock(&Winthread.lock); + + if (connection_status != TOX_CONNECTION_NONE) { int colour = MAGENTA; switch (status) { @@ -939,7 +962,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, A_BOLD); + pthread_mutex_lock(&Winthread.lock); wprintw(self->window, "%s", Friends.list[f].name); + pthread_mutex_unlock(&Winthread.lock); wattroff(self->window, A_BOLD); if (f_selected) @@ -952,17 +977,23 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) pthread_mutex_lock(&Winthread.lock); tox_friend_get_status_message(m, Friends.list[f].num, (uint8_t *) statusmsg, NULL); size_t s_len = tox_friend_get_status_message_size(m, Friends.list[f].num, NULL); - statusmsg[s_len] = '\0'; pthread_mutex_unlock(&Winthread.lock); + statusmsg[s_len] = '\0'; + filter_str(statusmsg, s_len); + + pthread_mutex_lock(&Winthread.lock); snprintf(Friends.list[f].statusmsg, sizeof(Friends.list[f].statusmsg), "%s", statusmsg); Friends.list[f].statusmsg_len = strlen(Friends.list[f].statusmsg); + pthread_mutex_unlock(&Winthread.lock); } /* Truncate note if it doesn't fit on one line */ size_t maxlen = x2 - getcurx(self->window) - 2; + pthread_mutex_lock(&Winthread.lock); + if (Friends.list[f].statusmsg_len > maxlen) { Friends.list[f].statusmsg[maxlen - 3] = '\0'; strcat(Friends.list[f].statusmsg, "..."); @@ -973,6 +1004,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) if (Friends.list[f].statusmsg_len > 0) wprintw(self->window, " %s", Friends.list[f].statusmsg); + pthread_mutex_unlock(&Winthread.lock); + wprintw(self->window, "\n"); } else { wprintw(self->window, "%s ", OFFLINE_CHAR); @@ -981,21 +1014,29 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, A_BOLD); + pthread_mutex_lock(&Winthread.lock); wprintw(self->window, "%s", Friends.list[f].name); + pthread_mutex_unlock(&Winthread.lock); wattroff(self->window, A_BOLD); if (f_selected) wattroff(self->window, COLOR_PAIR(BLUE)); + pthread_mutex_lock(&Winthread.lock); uint64_t last_seen = Friends.list[f].last_online.last_on; + pthread_mutex_unlock(&Winthread.lock); if (last_seen != 0) { + pthread_mutex_lock(&Winthread.lock); + int day_dist = ( cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday + ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365) ); const char *hourmin = Friends.list[f].last_online.hour_min_str; + pthread_mutex_unlock(&Winthread.lock); + switch (day_dist) { case 0: wprintw(self->window, " Last seen: Today %s\n", hourmin); @@ -1018,7 +1059,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m) self->x = x2; - if (Friends.num_friends) { + if (num_friends) { wmove(self->window, y2 - 1, 1); wattron(self->window, A_BOLD); diff --git a/src/global_commands.c b/src/global_commands.c index d2787e0..b9af59c 100644 --- a/src/global_commands.c +++ b/src/global_commands.c @@ -249,19 +249,22 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv) const char *ip = argv[1]; const char *port = argv[2]; - const char *key = argv[3]; + const char *ascii_key = argv[3]; if (atoi(port) == 0) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port."); return; } - char *binary_string = hex_string_to_bin(key); + char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; + if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); + return; + } TOX_ERR_BOOTSTRAP err; - tox_bootstrap(m, ip, atoi(port), (uint8_t *) binary_string, &err); - tox_add_tcp_relay(m, ip, atoi(port), (uint8_t *) binary_string, &err); - free(binary_string); + tox_bootstrap(m, ip, atoi(port), (uint8_t *) key_binary, &err); + tox_add_tcp_relay(m, ip, atoi(port), (uint8_t *) key_binary, &err); switch (err) { case TOX_ERR_BOOTSTRAP_BAD_HOST: @@ -457,16 +460,18 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX char myid[TOX_ADDRESS_SIZE]; tox_self_get_address(m, (uint8_t *) myid); + int log_ret = -1; + if (self->is_chat) { Friends.list[self->num].logging_on = true; - log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); + log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT); } else if (self->is_prompt) { - log_enable(self->name, myid, NULL, log, LOG_PROMPT); + log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT); } else if (self->is_groupchat) { - log_enable(self->name, myid, NULL, log, LOG_GROUP); + log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP); } - msg = "Logging enabled"; + msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize."; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); return; } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { @@ -475,7 +480,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX log_disable(log); - msg = "Logging disabled"; + msg = "Logging disabled."; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); return; } diff --git a/src/groupchat.c b/src/groupchat.c index 3734620..490081a 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -914,7 +914,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (diff != -1) { if (x + diff > x2 - 1) { - int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { @@ -1056,7 +1056,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m) int y, x; getyx(self->window, y, x); (void) x; - int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); + int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); wmove(self->window, y + 1, new_x); wrefresh(self->window); @@ -1087,7 +1087,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m) if (user_settings->autolog == AUTOLOG_ON) { char myid[TOX_ADDRESS_SIZE]; tox_self_get_address(m, (uint8_t *) myid); - log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP); + + if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1) + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); } execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); diff --git a/src/input.c b/src/input.c index 86f94f1..4186c4d 100644 --- a/src/input.c +++ b/src/input.c @@ -125,7 +125,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x) if (x + yank_cols >= mx_x) { int rmdr = MAX(0, (x + yank_cols) - mx_x); - int s_len = wcswidth(&ctx->line[ctx->start], rmdr); + int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr)); ctx->start += s_len + 1; } } @@ -137,7 +137,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x) ctx->pos = ctx->len; - int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x)); } @@ -196,7 +196,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x) ChatContext *ctx = self->chatwin; fetch_hist_item(ctx, key); - int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1; } diff --git a/src/line_info.c b/src/line_info.c index 1ea7b6e..67a6715 100644 --- a/src/line_info.c +++ b/src/line_info.c @@ -133,6 +133,9 @@ static struct line_info *line_info_ret_queue(struct history *hst) void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, uint8_t bold, uint8_t colour, const char *msg, ...) { + if (!self) + return; + struct history *hst = self->chatwin->hst; if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) @@ -332,13 +335,17 @@ void line_info_print(ToxWindow *self) if (line->msg[0] == '>') wattron(win, COLOR_PAIR(GREEN)); + else if (line->msg[0] == '<') + wattron(win, COLOR_PAIR(RED)); wprintw(win, "%s", line->msg); if (line->msg[0] == '>') wattroff(win, COLOR_PAIR(GREEN)); + else if (line->msg[0] == '<') + wattroff(win, COLOR_PAIR(RED)); - if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) { + if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { wattron(win, COLOR_PAIR(RED)); wprintw(win, " x", line->msg); wattroff(win, COLOR_PAIR(RED)); @@ -365,7 +372,7 @@ void line_info_print(ToxWindow *self) wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg); wattroff(win, COLOR_PAIR(YELLOW)); - if (type == OUT_ACTION && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) { + if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { wattron(win, COLOR_PAIR(RED)); wprintw(win, " x", line->msg); wattroff(win, COLOR_PAIR(RED)); diff --git a/src/log.c b/src/log.c index f04c83d..2d48dbc 100644 --- a/src/log.c +++ b/src/log.c @@ -139,11 +139,9 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e strftime(s, MAX_STR_SIZE, t, get_time()); fprintf(log->file, "%s %s %s\n", s, name_frmt, msg); - uint64_t curtime = get_unix_time(); - - if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) { + if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) { fflush(log->file); - log->lastwrite = curtime; + log->lastwrite = get_unix_time(); } } @@ -155,15 +153,19 @@ void log_disable(struct chatlog *log) memset(log, 0, sizeof(struct chatlog)); } -void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) +int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) { log->log_on = true; if (log->file != NULL) - return; + return 0; - if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) + if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { log_disable(log); + return -1; + } + + return 0; } /* Loads previous history from chat log */ @@ -177,7 +179,7 @@ void load_chat_history(ToxWindow *self, struct chatlog *log) if (sz <= 0) return; - char *hstbuf = malloc(sz); + char *hstbuf = malloc(sz + 1); if (hstbuf == NULL) exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); @@ -194,6 +196,8 @@ void load_chat_history(ToxWindow *self, struct chatlog *log) return; } + hstbuf[sz] = '\0'; + /* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */ int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size); int start, count = 0; diff --git a/src/log.h b/src/log.h index 103aafd..4bfa708 100644 --- a/src/log.h +++ b/src/log.h @@ -39,8 +39,12 @@ enum { /* formats/writes line to log file */ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event); -/* enables logging for specified log and creates/fetches file if necessary */ -void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); +/* enables logging for specified log and creates/fetches file if necessary. + * + * Returns 0 on success. + * Returns -1 on failure. + */ +int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype); /* disables logging for specified log and closes file */ void log_disable(struct chatlog *log); diff --git a/src/message_queue.c b/src/message_queue.c index 70ccadb..881e512 100644 --- a/src/message_queue.c +++ b/src/message_queue.c @@ -140,9 +140,7 @@ void cqueue_try_send(ToxWindow *self, Tox *m) if (!msg) return; - uint64_t curtime = get_unix_time(); - - if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL)) + if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) return; uint32_t receipt = 0; @@ -150,7 +148,7 @@ void cqueue_try_send(ToxWindow *self, Tox *m) TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION; receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL); - msg->last_send_try = curtime; + msg->last_send_try = get_unix_time(); msg->receipt = receipt; return; } diff --git a/src/misc_tools.c b/src/misc_tools.c index 7fc34aa..5487806 100644 --- a/src/misc_tools.c +++ b/src/misc_tools.c @@ -54,6 +54,7 @@ void hst_to_net(uint8_t *num, uint16_t numbytes) return; } +/* Note: The time functions are not thread safe */ void update_unix_time(void) { current_unix_time = (uint64_t) time(NULL); @@ -65,9 +66,9 @@ uint64_t get_unix_time(void) } /* Returns 1 if connection has timed out, 0 otherwise */ -int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout) +int timed_out(uint64_t timestamp, uint64_t timeout) { - return timestamp + timeout <= curtime; + return timestamp + timeout <= get_unix_time(); } /* Get the current local time */ @@ -109,20 +110,24 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs) snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); } -char *hex_string_to_bin(const char *hex_string) +/* + * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. + * output_size must be exactly half of hex_len. + * + * Returns 0 on success. + * Returns -1 on failure. + */ +int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size) { - size_t len = strlen(hex_string); - char *val = malloc(len); + if (output_size == 0 || hex_len != output_size * 2) + return -1; - if (val == NULL) - exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY); + for (size_t i = 0; i < output_size; ++i) { + sscanf(hex_string, "%2hhx", &output[i]); + hex_string += 2; + } - size_t i; - - for (i = 0; i < len; ++i, hex_string += 2) - sscanf(hex_string, "%2hhx", &val[i]); - - return val; + return 0; } int hex_string_to_bytes(char *buf, int size, const char *keystr) @@ -147,6 +152,9 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr) /* Returns 1 if the string is empty, 0 otherwise */ int string_is_empty(const char *string) { + if (!string) + return true; + return string[0] == '\0'; } @@ -217,7 +225,7 @@ void filter_str(char *str, size_t len) size_t i; for (i = 0; i < len; ++i) { - if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v') + if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') str[i] = ' '; } } diff --git a/src/misc_tools.h b/src/misc_tools.h index 3a5ca09..44ce5f0 100644 --- a/src/misc_tools.h +++ b/src/misc_tools.h @@ -41,22 +41,28 @@ void hst_to_net(uint8_t *num, uint16_t numbytes); -/* convert a hex string to binary */ -char *hex_string_to_bin(const char *hex_string); +/* + * Converts a hexidecimal string of length hex_len to binary format and puts the result in output. + * output_size must be exactly half of hex_len. + * + * Returns 0 on success. + * Returns -1 on failure. + */ +int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size); /* convert a hex string to bytes. returns 0 on success, -1 on failure */ int hex_string_to_bytes(char *buf, int size, const char *keystr); -/* get the current unix time */ +/* get the current unix time (not thread safe) */ uint64_t get_unix_time(void); -/* Puts the current time in buf in the format of [HH:mm:ss] */ +/* 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); -/* get the current local time */ +/* 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) */ @@ -75,7 +81,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, uint64_t curtime); +int timed_out(uint64_t timestamp, uint64_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/notify.c b/src/notify.c index 8560659..b7aae76 100644 --- a/src/notify.c +++ b/src/notify.c @@ -90,7 +90,7 @@ struct _ActiveNotifications { #ifdef BOX_NOTIFY NotifyNotification* box; char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1]; - char title[24]; + char title[64]; size_t size; time_t n_timeout; #endif @@ -117,9 +117,12 @@ static void tab_notify(ToxWindow *self, uint64_t flags) static bool notifications_are_disabled(uint64_t flags) { - bool res = flags & NT_RESTOL && Control.cooldown > get_unix_time(); + if (user_settings->alerts != ALERTS_ENABLED) + return true; + + bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time()); #ifdef X11 - return res || (flags & NT_NOFOCUS && is_focused()); + return res || ((flags & NT_NOFOCUS) && is_focused()); #else return res; #endif @@ -153,33 +156,36 @@ bool is_playing(int source) static bool device_opened = false; time_t last_opened_update = 0; -bool m_open_device() +/* Opens primary device. Returns true on succe*/ +void m_open_device() { last_opened_update = get_unix_time(); - if (device_opened) return true; + if (device_opened) return; /* Blah error check */ open_primary_device(output, &Control.device_idx, 48000, 20, 1); - return (device_opened = true); + device_opened = true; } -bool m_close_device() +void m_close_device() { - if (!device_opened) return true; + if (!device_opened) return; close_device(output, Control.device_idx); - return !(device_opened = false); + device_opened = false; } /* Terminate all sounds but wait for them to finish first */ void graceful_clear() { - int i; control_lock(); + while (1) { + int i; + for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { if (actives[i].active) { #ifdef BOX_NOTIFY @@ -211,18 +217,24 @@ void graceful_clear() usleep(1000); } + + control_unlock(); } void* do_playing(void* _p) { (void)_p; - int i; - bool has_looping = false; - - while(Control.poll_active) { + while(true) { control_lock(); + if (!Control.poll_active) { + control_unlock(); + break; + } + + bool has_looping = false; + int i; for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { @@ -245,7 +257,7 @@ void* do_playing(void* _p) } } #ifdef BOX_NOTIFY - else if (actives[i].box && get_unix_time() >= actives[i].n_timeout) + else if (actives[i].box && time(NULL) >= actives[i].n_timeout) { GError* ignore; notify_notification_close(actives[i].box, &ignore); @@ -266,7 +278,7 @@ void* do_playing(void* _p) /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ if (device_opened && !has_looping && - (get_unix_time() - last_opened_update) > DEVICE_COOLDOWN) { + (time(NULL) - last_opened_update) > DEVICE_COOLDOWN) { m_close_device(); } has_looping = false; @@ -299,11 +311,19 @@ int play_source(uint32_t source, uint32_t buffer, bool looping) void* do_playing(void* _p) { (void)_p; - int i; - while(Control.poll_active) { + + while(true) { control_lock(); + + if (!Control.poll_active) { + control_unlock(); + break; + } + + int i; + for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { - if (actives[i].box && get_unix_time() >= actives[i].n_timeout) + if (actives[i].box && time(NULL) >= actives[i].n_timeout) { GError* ignore; notify_notification_close(actives[i].box, &ignore); @@ -361,16 +381,17 @@ int init_notify(int login_cooldown, int notification_timeout) if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) return -1; + Control.poll_active = 1; pthread_t thread; if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) { pthread_mutex_destroy(Control.poll_mutex); + Control.poll_active = 0; return -1; } - Control.poll_active = 1; #endif - Control.cooldown = get_unix_time() + login_cooldown; + Control.cooldown = time(NULL) + login_cooldown; #ifdef BOX_NOTIFY @@ -383,8 +404,15 @@ int init_notify(int login_cooldown, int notification_timeout) void terminate_notify() { #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) - if ( !Control.poll_active ) return; + control_lock(); + + if ( !Control.poll_active ) { + control_unlock(); + return; + } + Control.poll_active = 0; + control_unlock(); graceful_clear(); #endif @@ -503,7 +531,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in int id = -1; control_lock(); - if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY) && user_settings->alerts == ALERTS_ENABLED) + if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) id = m_play_sound(notif, flags); else if (flags & NT_ALWAYS) id = m_play_sound(notif, flags); @@ -598,9 +626,12 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi actives[id].id_indicator = id_indicator; if (id_indicator) *id_indicator = id; } -#endif +#else + if (id == -1) + return -1; +#endif /* SOUND_NOTIFY */ - strncpy(actives[id].title, title, 24); + snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); va_list __ARGS__; va_start (__ARGS__, format); @@ -623,7 +654,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi return id; #else return sound_notify(self, notif, flags, id_indicator); -#endif +#endif /* BOX_NOTIFY */ } int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...) @@ -699,7 +730,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const *id_indicator = id; } - strncpy(actives[id].title, title, 24); + snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); va_list __ARGS__; va_start (__ARGS__, format); diff --git a/src/prompt.c b/src/prompt.c index 6299377..e080058 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -213,7 +213,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (diff != -1) { if (x + diff > x2 - 1) { - int wlen = wcswidth(ctx->line, sizeof(ctx->line)); + int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t))); ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { @@ -261,14 +261,23 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); StatusBar *statusbar = self->stb; + mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2); wmove(statusbar->topline, 0, 0); - if (statusbar->connection != TOX_CONNECTION_NONE) { + pthread_mutex_lock(&Winthread.lock); + TOX_CONNECTION connection = statusbar->connection; + pthread_mutex_unlock(&Winthread.lock); + + if (connection != TOX_CONNECTION_NONE) { int colour = MAGENTA; const char *status_text = "ERROR"; - switch (statusbar->status) { + pthread_mutex_lock(&Winthread.lock); + TOX_USER_STATUS status = statusbar->status; + pthread_mutex_unlock(&Winthread.lock); + + switch (status) { case TOX_USER_STATUS_NONE: status_text = "Online"; colour = GREEN; @@ -288,12 +297,16 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wattron(statusbar->topline, A_BOLD); + pthread_mutex_lock(&Winthread.lock); wprintw(statusbar->topline, " %s", statusbar->nick); + pthread_mutex_unlock(&Winthread.lock); wattroff(statusbar->topline, A_BOLD); } else { wprintw(statusbar->topline, " [Offline]"); wattron(statusbar->topline, A_BOLD); + pthread_mutex_lock(&Winthread.lock); wprintw(statusbar->topline, " %s", statusbar->nick); + pthread_mutex_unlock(&Winthread.lock); wattroff(statusbar->topline, A_BOLD); } @@ -305,10 +318,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) size_t slen = tox_self_get_status_message_size(m); tox_self_get_status_message (m, (uint8_t*) statusmsg); statusmsg[slen] = '\0'; - pthread_mutex_unlock(&Winthread.lock); - snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); statusbar->statusmsg_len = strlen(statusbar->statusmsg); + pthread_mutex_unlock(&Winthread.lock); } self->x = x2; @@ -316,6 +328,8 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) /* Truncate note if it doesn't fit in statusbar */ uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3; + pthread_mutex_lock(&Winthread.lock); + if (statusbar->statusmsg_len > maxlen) { statusbar->statusmsg[maxlen - 3] = '\0'; strcat(statusbar->statusmsg, "..."); @@ -325,13 +339,15 @@ static void prompt_onDraw(ToxWindow *self, Tox *m) if (statusbar->statusmsg[0]) wprintw(statusbar->topline, " : %s", statusbar->statusmsg); + pthread_mutex_unlock(&Winthread.lock); + mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); int y, x; getyx(self->window, y, x); (void) x; - int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos); + int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); wmove(self->window, y + 1, new_x); wrefresh(self->window); @@ -478,7 +494,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m) if (user_settings->autolog == AUTOLOG_ON) { char myid[TOX_ADDRESS_SIZE]; tox_self_get_address(m, (uint8_t *) myid); - log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT); + + if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) + line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); } scrollok(ctx->history, 0); diff --git a/src/prompt.h b/src/prompt.h index 4842f6c..1917375 100644 --- a/src/prompt.h +++ b/src/prompt.h @@ -41,6 +41,7 @@ typedef struct { } FriendRequests; ToxWindow new_prompt(void); + void prep_prompt_win(void); void prompt_init_statusbar(ToxWindow *self, Tox *m); void prompt_update_nick(ToxWindow *prompt, const char *nick); diff --git a/src/term_mplex.c b/src/term_mplex.c index 130e868..35bf082 100644 --- a/src/term_mplex.c +++ b/src/term_mplex.c @@ -167,6 +167,10 @@ static int detect_gnu_screen () free (dyn_buffer); dyn_buffer = NULL; + + if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data)) + goto nomplex; + strcpy (mplex_data, socket_path); strcat (mplex_data, PATH_SEP_S); strcat (mplex_data, socket_name); @@ -181,6 +185,8 @@ nomplex: pclose (session_info_stream); if (dyn_buffer) free (dyn_buffer); + if (socket_path) + free(socket_path); return 0; } @@ -196,7 +202,7 @@ static int detect_tmux () return 0; /* store the session number string for later use */ - strcpy (mplex_data, pos + 1); + snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1); mplex = MPLEX_TMUX; return 1; } diff --git a/src/toxic.c b/src/toxic.c index 4b93c7c..41d606f 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -78,6 +78,9 @@ char *DATA_FILE = NULL; char *BLOCK_FILE = NULL; ToxWindow *prompt = NULL; +#define DATANAME "data" +#define BLOCKNAME "data-blocklist" + #define AUTOSAVE_FREQ 60 #define MIN_PASSWORD_LEN 6 #define MAX_PASSWORD_LEN 64 @@ -120,6 +123,24 @@ static void init_signal_catchers(void) signal(SIGSEGV, catch_SIGSEGV); } +void free_global_data(void) +{ + if (DATA_FILE) { + free(DATA_FILE); + DATA_FILE = NULL; + } + + if (BLOCK_FILE) { + free(BLOCK_FILE); + BLOCK_FILE = NULL; + } + + if (user_settings) { + free(user_settings); + user_settings = NULL; + } +} + void exit_toxic_success(Tox *m) { store_data(m, DATA_FILE); @@ -132,10 +153,7 @@ void exit_toxic_success(Tox *m) terminate_audio(); #endif /* AUDIO */ - free(DATA_FILE); - free(BLOCK_FILE); - free(user_settings); - + free_global_data(); tox_kill(m); endwin(); @@ -151,6 +169,7 @@ void exit_toxic_success(Tox *m) void exit_toxic_err(const char *errmsg, int errcode) { + free_global_data(); freopen("/dev/tty", "w", stderr); endwin(); fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg); @@ -277,22 +296,36 @@ static int load_nodelist(const char *filename) char line[MAX_NODE_LINE]; while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) { - if (strlen(line) > MIN_NODE_LINE) { + 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 = strtok(NULL, " "); const char *key_ascii = strtok(NULL, " "); - /* invalid line */ if (name == NULL || port == NULL || key_ascii == NULL) 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] = atoi(port); - char *key_binary = hex_string_to_bin(key_ascii); + /* remove possible trailing newline from key string */ + char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; + memcpy(key_binary, key_ascii, TOX_PUBLIC_KEY_SIZE * 2); + key_len = TOX_PUBLIC_KEY_SIZE * 2; + key_binary[key_len] = '\0'; + + if (hex_string_to_bin(key_ascii, key_len, key_binary, TOX_PUBLIC_KEY_SIZE) == -1) + continue; + memcpy(toxNodes.keys[toxNodes.lines], key_binary, TOX_PUBLIC_KEY_SIZE); - free(key_binary); toxNodes.lines++; } @@ -777,9 +810,8 @@ static uint64_t last_bootstrap_time = 0; static void do_bootstrap(Tox *m) { static int conn_err = 0; - uint64_t curtime = get_unix_time(); - if (!timed_out(last_bootstrap_time, curtime, TRY_BOOTSTRAP_INTERVAL)) + if (!timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) return; if (tox_self_get_connection_status(m) != TOX_CONNECTION_NONE) @@ -788,7 +820,7 @@ static void do_bootstrap(Tox *m) if (conn_err != 0) return; - last_bootstrap_time = curtime; + last_bootstrap_time = get_unix_time(); conn_err = init_connection(m); if (conn_err != 0) @@ -797,12 +829,17 @@ static void do_bootstrap(Tox *m) static void do_toxic(Tox *m, ToxWindow *prompt) { - if (arg_opts.no_connect) - return; - pthread_mutex_lock(&Winthread.lock); + update_unix_time(); + + if (arg_opts.no_connect) { + pthread_mutex_unlock(&Winthread.lock); + return; + } + tox_iterate(m); do_bootstrap(m); + check_file_transfer_timeouts(m); pthread_mutex_unlock(&Winthread.lock); } @@ -811,6 +848,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt) void *thread_winref(void *data) { Tox *m = (Tox *) data; + uint8_t draw_count = 0; init_signal_catchers(); @@ -955,10 +993,22 @@ static void parse_args(int argc, char *argv[]) case 'f': arg_opts.use_custom_data = 1; - DATA_FILE = strdup(optarg); + + if (DATA_FILE) + free(DATA_FILE); + + if (BLOCK_FILE) + free(BLOCK_FILE); + + DATA_FILE = malloc(strlen(optarg) + 1); + strcpy(DATA_FILE, optarg); + + if (DATA_FILE == NULL) + exit_toxic_err("failed in parse_args", FATALERR_MEMORY); + BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1); - if (DATA_FILE == NULL || BLOCK_FILE == NULL) + if (BLOCK_FILE == NULL) exit_toxic_err("failed in parse_args", FATALERR_MEMORY); strcpy(BLOCK_FILE, optarg); @@ -1029,8 +1079,6 @@ static void parse_args(int argc, char *argv[]) } } -#define DATANAME "data" -#define BLOCKNAME "data-blocklist" static int init_default_data_files(void) { if (arg_opts.use_custom_data) @@ -1070,7 +1118,7 @@ static int init_default_data_files(void) /* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */ static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval) { - useconds_t new_sleep = msleepval; + useconds_t new_sleep = MAX(msleepval, 3); ++(*loopcount); if (*looptimer == cur_time) @@ -1084,14 +1132,16 @@ static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, ui return new_sleep; } +// this doesn't do anything (yet) #ifdef X11 -// FIXME void DnD_callback(const char* asdv, DropType dt) { - if (dt != DT_plain) - return; + // if (dt != DT_plain) + // return; - line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv); + // pthread_mutex_lock(&Winthread.lock); + // line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv); + pthread_mutex_unlock(&Winthread.lock); } #endif /* X11 */ @@ -1160,6 +1210,7 @@ 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); @@ -1193,8 +1244,11 @@ int main(int argc, char *argv[]) if (init_mplex_away_timer(m) == -1) queue_init_message("Failed to init mplex auto-away."); + pthread_mutex_lock(&Winthread.lock); load_groups(m); print_init_messages(prompt); + pthread_mutex_unlock(&Winthread.lock); + cleanup_init_messages(); /* set user avatar from config file. if no path is supplied tox_unset_avatar is called */ @@ -1208,11 +1262,10 @@ int main(int argc, char *argv[]) uint64_t loopcount = 0; while (true) { - update_unix_time(); do_toxic(m, prompt); uint64_t cur_time = get_unix_time(); - if (timed_out(last_save, cur_time, AUTOSAVE_FREQ)) { + if (timed_out(last_save, AUTOSAVE_FREQ)) { pthread_mutex_lock(&Winthread.lock); if (store_data(m, DATA_FILE) != 0) line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); diff --git a/src/windows.c b/src/windows.c index e91a24f..0cd5c1b 100644 --- a/src/windows.c +++ b/src/windows.c @@ -569,10 +569,16 @@ void on_window_resize(void) static void draw_window_tab(ToxWindow *toxwin) { + pthread_mutex_lock(&Winthread.lock); if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert)); + pthread_mutex_unlock(&Winthread.lock); + clrtoeol(); printw(" [%s]", toxwin->name); + + pthread_mutex_lock(&Winthread.lock); if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert)); + pthread_mutex_unlock(&Winthread.lock); } static void draw_bar(void) @@ -620,7 +626,10 @@ static void draw_bar(void) void draw_active_window(Tox *m) { ToxWindow *a = active_window; + + pthread_mutex_lock(&Winthread.lock); a->alert = WINDOW_ALERT_NONE; + pthread_mutex_unlock(&Winthread.lock); wint_t ch = 0; @@ -683,7 +692,7 @@ ToxWindow *get_window_ptr(int i) { ToxWindow *toxwin = NULL; - if (i >= 0 && i <= MAX_WINDOWS_NUM && windows[i].active) + if (i >= 0 && i < MAX_WINDOWS_NUM && windows[i].active) toxwin = &windows[i]; return toxwin; diff --git a/src/xtra.c b/src/xtra.c index 51eeb86..05004c3 100644 --- a/src/xtra.c +++ b/src/xtra.c @@ -72,24 +72,24 @@ Property read_property(Window s, Atom p) unsigned long read_num; unsigned long left_bytes; unsigned char *data = NULL; - + int read_bytes = 1024; - + /* Keep trying to read the property until there are no bytes unread */ do { if (data) XFree(data); - - XGetWindowProperty(Xtra.display, s, - p, 0, - read_bytes, + + XGetWindowProperty(Xtra.display, s, + p, 0, + read_bytes, False, AnyPropertyType, - &read_type, &read_format, - &read_num, &left_bytes, + &read_type, &read_format, + &read_num, &left_bytes, &data); - + read_bytes *= 2; } while (left_bytes != 0); - + Property property = {data, read_format, read_num, read_type}; return property; } @@ -107,7 +107,7 @@ Atom get_dnd_type(long *a, int l) static void handle_xdnd_enter(XClientMessageEvent* e) { Xtra.handling_version = (e->data.l[1] >> 24); - + if ((e->data.l[1] & 1)) { // Fetch the list of possible conversions Property p = read_property(e->data.l[0], XdndTypeList); @@ -120,7 +120,7 @@ static void handle_xdnd_enter(XClientMessageEvent* e) } static void handle_xdnd_position(XClientMessageEvent* e) -{ +{ XEvent ev = { .xclient = { .type = ClientMessage, @@ -130,15 +130,15 @@ static void handle_xdnd_position(XClientMessageEvent* e) .format = 32, .data = { .l = { - Xtra.proxy_window, - (Xtra.expecting_type != XtraNil), - 0, 0, + Xtra.proxy_window, + (Xtra.expecting_type != XtraNil), + 0, 0, XdndActionCopy } } } }; - + XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev); XFlush(Xtra.display); } @@ -159,7 +159,7 @@ static void handle_xdnd_drop(XClientMessageEvent* e) } } }; - + XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev); } else { Xtra.source_window = e->data.l[0]; @@ -188,57 +188,57 @@ static void handle_xdnd_selection(XSelectionEvent* e) } }; XSendEvent(Xtra.display, Xtra.source_window, False, NoEventMask, &ev); - + Property p = read_property(Xtra.proxy_window, XdndSelection); DropType dt; - + if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) dt = DT_file_list; else /* text/uri-list */ dt = DT_plain; - - + + /* Call callback for every entry */ if (Xtra.on_drop && p.read_num) { char *sptr; char *str = strtok_r((char *) p.data, "\n\r", &sptr); - + if (str) Xtra.on_drop(str, dt); while ((str = strtok_r(NULL, "\n\r", &sptr))) Xtra.on_drop(str, dt); } - + if (p.data) XFree(p.data); } void *event_loop(void* p) { /* Handle events like a real nigga */ - + (void) p; /* DINDUNOTHIN */ - + XEvent event; int pending; - + while (Xtra.display) { /* NEEDMOEVENTSFODEMPROGRAMS */ - + XLockDisplay(Xtra.display); if((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event); - + if (!pending) { XUnlockDisplay(Xtra.display); usleep(10000); continue; } - + if (event.type == ClientMessage) { Atom type = event.xclient.message_type; - + if (type == XdndEnter) handle_xdnd_enter(&event.xclient); else if (type == XdndPosition) handle_xdnd_position(&event.xclient); else if (type == XdndDrop) handle_xdnd_drop(&event.xclient); @@ -247,11 +247,11 @@ void *event_loop(void* p) else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection); /* AINNOBODYCANHANDLEDEMEVENTS*/ else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event); - + XUnlockDisplay(Xtra.display); } - - /* Actual XTRA termination + + /* Actual XTRA termination * Please call xtra_terminate() at exit * otherwise HEWUSAGUDBOI happens */ @@ -262,23 +262,23 @@ void *event_loop(void* p) int init_xtra(drop_callback d) { memset(&Xtra, 0, sizeof(Xtra)); - + if (!d) return -1; else Xtra.on_drop = d; - + XInitThreads(); if ( !(Xtra.display = XOpenDisplay(NULL))) return -1; - + Xtra.terminal_window = focused_window_id(); - + { /* Create an invisible window which will act as proxy for the DnD operation. */ XSetWindowAttributes attr = {0}; - attr.event_mask = EnterWindowMask | - LeaveWindowMask | - ButtonMotionMask | - ButtonPressMask | - ButtonReleaseMask | + attr.event_mask = EnterWindowMask | + LeaveWindowMask | + ButtonMotionMask | + ButtonPressMask | + ButtonReleaseMask | ResizeRedirectMask; attr.do_not_propagate_mask = NoEventMask; @@ -286,14 +286,14 @@ int init_xtra(drop_callback d) Window root; int x, y; unsigned int wht, hht, b, d; - + /* Since we cannot capture resize events for parent window we will have to create - * this window to have maximum size as defined in root window + * this window to have maximum size as defined in root window */ XGetGeometry(Xtra.display, XDefaultRootWindow(Xtra.display), &root, &x, &y, &wht, &hht, &b, &d); - + if (! (Xtra.proxy_window = XCreateWindow (Xtra.display, Xtra.terminal_window, /* Parent */ 0, 0, /* Position */ @@ -306,7 +306,7 @@ int init_xtra(drop_callback d) &attr)) ) /* Attributes for value mask */ return -1; } - + XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */ XLowerWindow(Xtra.display, Xtra.proxy_window); /* Don't interfere with parent lmao */ @@ -321,7 +321,7 @@ int init_xtra(drop_callback d) XdndTypeList = XInternAtom(Xtra.display, "XdndTypeList", False); XdndActionCopy = XInternAtom(Xtra.display, "XdndActionCopy", False); XdndFinished = XInternAtom(Xtra.display, "XdndFinished", False); - + /* Inform my nigga windows that we are aware of dnd */ Atom XdndVersion = 3; XChangeProperty(Xtra.display, @@ -331,18 +331,20 @@ int init_xtra(drop_callback d) 32, PropModeReplace, (unsigned char*)&XdndVersion, 1); - + pthread_t id; - pthread_create(&id, NULL, event_loop, NULL); + if (pthread_create(&id, NULL, event_loop, NULL) != 0) + return -1; + pthread_detach(id); - + return 0; } void terminate_xtra() { if (!Xtra.display) return; - + XEvent terminate = { .xclient = { .type = ClientMessage, @@ -350,19 +352,19 @@ void terminate_xtra() .message_type = XtraTerminate, } }; - + XLockDisplay(Xtra.display); XDeleteProperty(Xtra.display, Xtra.proxy_window, XdndAware); XSendEvent(Xtra.display, Xtra.proxy_window, 0, NoEventMask, &terminate); XUnlockDisplay(Xtra.display); - + while (Xtra.display); /* Wait for termination */ } long unsigned int focused_window_id() { if (!Xtra.display) return 0; - + Window focus; int revert; XLockDisplay(Xtra.display);