/* Get the current local time */ struct tm *get_time(void) { struct tm *timeinfo; uint64_t t = get_unix_time(); timeinfo = localtime((const time_t*) &t); return timeinfo; }
static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, int filenum, const char *filename) { FILE *fp = file_senders[i].file; while (true) { if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece, file_senders[i].piecelen) == -1) return; uint64_t curtime = get_unix_time(); file_senders[i].timestamp = curtime; file_senders[i].bps += file_senders[i].piecelen; file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, friendnum), fp); double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0); /* refresh line with percentage complete and transfer speed (must be called once per second) */ if (timed_out(file_senders[i].last_progress, curtime, 1) || (!remain && !file_senders[i].finished)) { file_senders[i].last_progress = curtime; double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100; print_progress_bar(self, i, -1, pct_done); file_senders[i].bps = 0; } /* file sender is closed in chat_onFileControl callback after receiving reply */ if (file_senders[i].piecelen == 0 && !file_senders[i].finished) { tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0); file_senders[i].finished = true; } } }
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */ void *group_add_wait(void *data) { struct group_add_thrd *thrd = (struct group_add_thrd *) data; ToxWindow *self = thrd->self; Tox *m = thrd->m; char peername[TOX_MAX_NAME_LENGTH]; /* keep polling for a name that differs from the default until we run out of time */ while (true) { usleep(100000); pthread_mutex_lock(&Winthread.lock); get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum); if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, get_unix_time(), GROUP_EVENT_WAIT)) { pthread_mutex_unlock(&Winthread.lock); break; } pthread_mutex_unlock(&Winthread.lock); } const char *event = "has joined the room"; char timefrmt[TIME_STR_SIZE]; get_time_str(timefrmt, sizeof(timefrmt)); pthread_mutex_lock(&Winthread.lock); line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event); write_to_log(event, (char *) peername, self->chatwin->log, true); pthread_mutex_unlock(&Winthread.lock); pthread_attr_destroy(&thrd->attr); free(thrd); pthread_exit(NULL); }
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) { if (!log->log_on) return; if (log->file == NULL) { log->log_on = false; return; } char name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; if (event) snprintf(name_frmt, sizeof(name_frmt), "* %s", name); else snprintf(name_frmt, sizeof(name_frmt), "%s:", name); const char *t = user_settings->log_timestamp_format; char s[MAX_STR_SIZE]; strftime(s, MAX_STR_SIZE, t, get_time()); fprintf(log->file, "%s %s %s\n", s, name_frmt, msg); if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) { fflush(log->file); log->lastwrite = get_unix_time(); } }
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *title, uint8_t length) { ChatContext *ctx = self->chatwin; if (self->num != groupnum) return; set_window_title(self, title, length); char timefrmt[TIME_STR_SIZE]; get_time_str(timefrmt, sizeof(timefrmt)); /* don't announce title when we join the room */ if (!timed_out(groupchats[self->num].start_time, get_unix_time(), GROUP_EVENT_WAIT)) return; char nick[TOX_MAX_NAME_LENGTH]; get_group_nick_truncate(m, nick, peernum, groupnum); line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title); char tmp_event[MAX_STR_SIZE]; snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); write_to_log(tmp_event, nick, ctx->log, true); }
void write_to_log(const uint8_t *msg, uint8_t *name, struct chatlog *log, bool event) { if (!log->log_on) return; if (log->file == NULL) { log->log_on = false; return; } uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; if (event) snprintf(name_frmt, sizeof(name_frmt), "* %s", name); else snprintf(name_frmt, sizeof(name_frmt), "%s:", name); uint8_t s[MAX_STR_SIZE]; strftime(s, MAX_STR_SIZE, "%Y/%m/%d [%H:%M:%S]", 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)) { fflush(log->file); log->lastwrite = curtime; } }
static void do_connection(Tox *m, ToxWindow *prompt) { char msg[MAX_STR_SIZE] = {0}; static int conn_err = 0; static bool was_connected = false; static uint64_t last_conn_try = 0; uint64_t curtime = get_unix_time(); bool is_connected = tox_isconnected(m); if (was_connected && is_connected) return; if (!was_connected && is_connected) { was_connected = true; prompt_update_connectionstatus(prompt, was_connected); snprintf(msg, sizeof(msg), "DHT connected."); } else if (was_connected && !is_connected) { was_connected = false; prompt_update_connectionstatus(prompt, was_connected); snprintf(msg, sizeof(msg), "DHT disconnected. Attempting to reconnect."); } else if (!was_connected && !is_connected && timed_out(last_conn_try, curtime, TRY_CONNECT)) { /* if autoconnect has already failed there's no point in trying again */ if (conn_err == 0) { last_conn_try = curtime; if ((conn_err = init_connection(m)) != 0) snprintf(msg, sizeof(msg), "Auto-connect failed with error code %d", conn_err); } } if (msg[0]) line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); }
static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum, const char *data, uint16_t length) { if (self->num != num) return; FILE *fp = friends[num].file_receiver.files[filenum]; if (fp) { if (fwrite(data, length, 1, fp) != 1) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Error writing to file."); tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0); chat_close_file_receiver(num, filenum); } } friends[num].file_receiver.bps[filenum] += length; double remain = (double) tox_file_data_remaining(m, num, filenum, 1); uint64_t curtime = get_unix_time(); /* refresh line with percentage complete and transfer speed (must be called once per second) */ if (!remain || timed_out(friends[num].file_receiver.last_progress[filenum], curtime, 1)) { friends[num].file_receiver.last_progress[filenum] = curtime; uint64_t size = friends[num].file_receiver.size[filenum]; double pct_remain = remain > 0 ? (1 - (remain / size)) * 100 : 100; print_progress_bar(self, filenum, num, pct_remain); friends[num].file_receiver.bps[filenum] = 0; } }
/** * @brief UNIX time shiznazzlage. */ static char *scrob_utime(char *dst, size_t sz) { unsigned long int utime; utime = get_unix_time(); snprintf(dst, sz, "%lu", utime); return dst; }
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...) { if (notifications_are_disabled(flags)) { tab_notify(self, flags); return -1; } #ifdef BOX_NOTIFY int id = sound_notify(self, notif, flags, id_indicator); control_lock(); #ifdef SOUND_NOTIFY if (id == -1) { /* Could not play */ for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); if ( id == ACTIVE_NOTIFS_MAX ) { control_unlock(); return -1; /* Full */ } actives[id].active = 1; actives[id].id_indicator = id_indicator; if (id_indicator) *id_indicator = id; } #else if (id == -1) return -1; #endif /* SOUND_NOTIFY */ 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); vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); va_end (__ARGS__); if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); actives[id].size++; actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; notify_notification_set_timeout(actives[id].box, Control.notif_timeout); notify_notification_set_app_name(actives[id].box, "toxic"); /*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/ notify_notification_show(actives[id].box, NULL); control_unlock(); return id; #else return sound_notify(self, notif, flags, id_indicator); #endif /* BOX_NOTIFY */ }
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status) { if (num >= Friends.max_idx) return; Friends.list[num].online = status; update_friend_last_online(num, get_unix_time()); store_data(m, DATA_FILE); sort_friendlist_index(); }
/* Opens primary device. Returns true on succe*/ void m_open_device() { last_opened_update = get_unix_time(); if (device_opened) return; /* Blah error check */ open_primary_device(output, &Control.device_idx, 48000, 20, 1); device_opened = true; }
static bool notifications_are_disabled(uint64_t flags) { 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()); #else return res; #endif }
/* update infobox info and draw in respective chat window */ static void draw_infobox(ToxWindow *self) { struct infobox *infobox = &self->chatwin->infobox; if (infobox->win == NULL) return; int x2, y2; getmaxyx(self->window, y2, x2); if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT) return; uint64_t curtime = get_unix_time(); /* update elapsed time string once per second */ if (curtime > infobox->lastupdate) get_elapsed_time_str(infobox->timestr, sizeof(infobox->timestr), curtime - infobox->starttime); infobox->lastupdate = curtime; const char *in_is_muted = infobox->in_is_muted ? "yes" : "no"; const char *out_is_muted = infobox->out_is_muted ? "yes" : "no"; wmove(infobox->win, 1, 1); wattron(infobox->win, COLOR_PAIR(RED) | A_BOLD); wprintw(infobox->win, " Call Active\n"); wattroff(infobox->win, COLOR_PAIR(RED) | A_BOLD); wattron(infobox->win, A_BOLD); wprintw(infobox->win, " Duration: "); wattroff(infobox->win, A_BOLD); wprintw(infobox->win, "%s\n", infobox->timestr); wattron(infobox->win, A_BOLD); wprintw(infobox->win, " In muted: "); wattroff(infobox->win, A_BOLD); wprintw(infobox->win, "%s\n", in_is_muted); wattron(infobox->win, A_BOLD); wprintw(infobox->win, " Out muted: "); wattroff(infobox->win, A_BOLD); wprintw(infobox->win, "%s\n", out_is_muted); wattron(infobox->win, A_BOLD); wprintw(infobox->win, " VAD level: "); wattroff(infobox->win, A_BOLD); wprintw(infobox->win, "%.2f\n", infobox->vad_lvl); wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' '); wrefresh(infobox->win); }
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...) { if (notifications_are_disabled(flags)) { tab_notify(self, flags); return -1; } #ifdef BOX_NOTIFY if (sound_notify2(self, notif, flags, id) == -1) return -1; control_lock(); if (!actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) { control_unlock(); return -1; } va_list __ARGS__; va_start (__ARGS__, format); vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); va_end (__ARGS__); if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); actives[id].size++; actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; char formated[128 * 129] = {'\0'}; int i = 0; for (; i <actives[id].size; i ++) { strcat(formated, actives[id].messages[i]); strcat(formated, "\n"); } formated[strlen(formated) - 1] = '\0'; notify_notification_update(actives[id].box, actives[id].title, formated, NULL); notify_notification_show(actives[id].box, NULL); control_unlock(); return id; #else return sound_notify2(self, notif, flags, id); #endif }
static void init_infobox(ToxWindow *self) { ChatContext *ctx = self->chatwin; int x2, y2; getmaxyx(self->window, y2, x2); (void) y2; memset(&ctx->infobox, 0, sizeof(struct infobox)); ctx->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); ctx->infobox.starttime = get_unix_time(); ctx->infobox.vad_lvl = user_settings_->VAD_treshold; ctx->infobox.active = true; strcpy(ctx->infobox.timestr, "00"); }
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_line_progress, 1)) return; double remain = ft->file_size - ft->position; double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100; print_progress_bar(self, ft->bps, pct_done, ft->line_id); ft->bps = 0; ft->last_line_progress = get_unix_time(); }
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type) { if (groupnum > MAX_GROUPCHAT_NUM) return -1; ToxWindow self = new_group_chat(m, groupnum); int i; for (i = 0; i <= max_groupchat_index; ++i) { if (!groupchats[i].active) { groupchats[i].chatwin = add_window(m, self); groupchats[i].active = true; groupchats[i].num_peers = 0; groupchats[i].type = type; groupchats[i].start_time = get_unix_time(); groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH); groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t)); groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t)); if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL || groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL) exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY); memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME); #ifdef AUDIO if (type == TOX_GROUPCHAT_TYPE_AV) { if (group_audio_open_out_device(i) != 0) fprintf(stderr, "audio failed\n"); } #endif /* AUDIO */ set_active_window(groupchats[i].chatwin); if (i == max_groupchat_index) ++max_groupchat_index; return 0; } } return -1; }
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status) { if (num >= Friends.max_idx) return; if (connection_status == TOX_CONNECTION_NONE) { --Friends.num_online; } else if (Friends.list[num].connection_status == TOX_CONNECTION_NONE) { ++Friends.num_online; if (avatar_send(m, num) == -1) fprintf(stderr, "avatar_send failed for friend %d\n", num); } Friends.list[num].connection_status = connection_status; update_friend_last_online(num, get_unix_time()); store_data(m, DATA_FILE); sort_friendlist_index(); }
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); }
void do_file_senders(Tox *m) { int i; for (i = 0; i < max_file_senders_index; ++i) { if (!file_senders[i].active) continue; if (file_senders[i].queue_pos > 0) { --file_senders[i].queue_pos; continue; } ToxWindow *self = file_senders[i].toxwin; char *filename = file_senders[i].filename; int filenum = file_senders[i].filenum; int32_t friendnum = file_senders[i].friendnum; /* kill file transfer if chatwindow is closed */ if (self->chatwin == NULL) { close_file_sender(self, m, i, NULL, TOX_FILECONTROL_KILL, filenum, friendnum); continue; } /* If file transfer has timed out kill transfer and send kill control */ if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) { char msg[MAX_STR_SIZE]; snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", filename); close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum); sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL); if (self->active_box != -1) box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg); else box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg); continue; } send_file_data(self, m, i, friendnum, filenum, filename); file_senders[i].queue_pos = num_active_file_senders - 1; } }
void start_observation(char* keyword, u8 field_cnt, u8 to_srv, struct packet_flow* f) { if (obs_fields) printf("\n FATAL: Premature end of observation."); if (!daemon_mode) { SAYF(".-[ %s/%u -> ", addr_to_str(f->client->addr, f->client->ip_ver), f->cli_port); SAYF("%s/%u (%s) ]-\n|\n", addr_to_str(f->server->addr, f->client->ip_ver), f->srv_port, keyword); SAYF("| %-8s = %s/%u\n", to_srv ? "client" : "server", addr_to_str(to_srv ? f->client->addr : f->server->addr, f->client->ip_ver), to_srv ? f->cli_port : f->srv_port); } if (log_file) { u8 tmp[64]; time_t ut = get_unix_time(); struct tm* lt = localtime(&ut); strftime((char*)tmp, 64, "%Y/%m/%d %H:%M:%S", lt); LOGF("[%s] mod=%s|cli=%s/%u|",tmp, keyword, addr_to_str(f->client->addr, f->client->ip_ver), f->cli_port); LOGF("srv=%s/%u|subj=%s", addr_to_str(f->server->addr, f->server->ip_ver), f->srv_port, to_srv ? "cli" : "srv"); } obs_fields = field_cnt; }
/* Returns a pointer to an unused file receiver. * Returns NULL if all file receivers are in use. */ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type) { size_t i; for (i = 0; i < MAX_FILES; ++i) { 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; } } return NULL; }
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); } } } } }
int main(int argc, char *argv[]) { char *user_config_dir = get_user_config_dir(); int config_err = 0; parse_args(argc, argv); /* Make sure all written files are read/writeable only by the current user. */ umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); signal(SIGINT, catch_SIGINT); config_err = create_user_config_dir(user_config_dir); if (DATA_FILE == NULL ) { if (config_err) { DATA_FILE = strdup("data"); if (DATA_FILE == NULL) exit_toxic_err("failed in main", FATALERR_MEMORY); } else { DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1); if (DATA_FILE == NULL) exit_toxic_err("failed in main", FATALERR_MEMORY); strcpy(DATA_FILE, user_config_dir); strcat(DATA_FILE, CONFIGDIR); strcat(DATA_FILE, "data"); } } free(user_config_dir); /* init user_settings struct and load settings from conf file */ user_settings_ = calloc(1, sizeof(struct user_settings)); if (user_settings_ == NULL) exit_toxic_err("failed in main", FATALERR_MEMORY); char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; int settings_err = settings_load(user_settings_, p); Tox *m = init_tox(arg_opts.use_ipv4); init_term(); if (m == NULL) exit_toxic_err("failed in main", FATALERR_NETWORKINIT); if (!arg_opts.ignore_data_file) load_data(m, DATA_FILE); prompt = init_windows(m); prompt_init_statusbar(prompt, m); /* thread for ncurses stuff */ if (pthread_mutex_init(&Winthread.lock, NULL) != 0) exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); #ifdef _AUDIO av = init_audio(prompt, m); set_primary_device(input, user_settings_->audio_in_dev); set_primary_device(output, user_settings_->audio_out_dev); #elif _SOUND_NOTIFY if ( init_devices() == de_InternalError ) line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); #endif /* _AUDIO */ init_notify(60, 3000); #ifdef _SOUND_NOTIFY notify(prompt, self_log_in, 0); #endif /* _SOUND_NOTIFY */ const char *msg; if (config_err) { msg = "Unable to determine configuration directory. Defaulting to 'data' for a keyfile..."; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); } if (settings_err == -1) { msg = "Failed to load user settings"; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); } sort_friendlist_index(); uint64_t last_save = (uint64_t) time(NULL); 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)) { pthread_mutex_lock(&Winthread.lock); store_data(m, DATA_FILE); pthread_mutex_unlock(&Winthread.lock); last_save = cur_time; } usleep(40000); } return 0; }
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change) { if (self->num != groupnum) return; if (groupnum > max_groupchat_index) return; groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum); int num_peers = groupchats[groupnum].num_peers; if (peernum > num_peers) return; /* get old peer name before updating name list */ uint8_t oldpeername[TOX_MAX_NAME_LENGTH]; if (change != TOX_CHAT_CHANGE_PEER_ADD) { memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(oldpeername)); uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum]; oldpeername[old_n_len] = '\0'; } /* Update name/len lists */ uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH]; uint16_t tmp_peerlens[num_peers]; if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) { memset(tmp_peerlist, 0, sizeof(tmp_peerlist)); memset(tmp_peerlens, 0, sizeof(tmp_peerlens)); } copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers); /* get current peername then sort namelist */ uint8_t peername[TOX_MAX_NAME_LENGTH]; if (change != TOX_CHAT_CHANGE_PEER_DEL) { uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum]; memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername)); peername[n_len] = '\0'; } qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr); ChatContext *ctx = self->chatwin; const char *event; char timefrmt[TIME_STR_SIZE]; get_time_str(timefrmt, sizeof(timefrmt)); switch (change) { case TOX_CHAT_CHANGE_PEER_ADD: if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT)) break; struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd)); thrd->m = m; thrd->peernum = peernum; thrd->groupnum = groupnum; thrd->self = self; thrd->timestamp = get_unix_time(); if (pthread_attr_init(&thrd->attr) != 0) { free(thrd); return; } if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) { pthread_attr_destroy(&thrd->attr); free(thrd); return; } if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) { pthread_attr_destroy(&thrd->attr); free(thrd); return; } break; case TOX_CHAT_CHANGE_PEER_DEL: event = "has left the room"; line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event); if (groupchats[self->num].side_pos > 0) --groupchats[self->num].side_pos; write_to_log(event, (char *) oldpeername, ctx->log, true); break; case TOX_CHAT_CHANGE_PEER_NAME: if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) return; /* ignore initial name change (TODO: this is a bad way to do this) */ if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0) return; event = " is now known as "; line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event); char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername); write_to_log(tmp_event, (char *) oldpeername, ctx->log, true); break; } sound_notify(self, silent, NT_WNDALERT_2, NULL); }
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { if (max_file_senders_index >= (MAX_FILES - 1)) { const char *errmsg = "Please wait for some of your outgoing file transfers to complete."; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg); return; } if (argc < 1) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required."); return; } if (argv[1][0] != '\"') { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes."); return; } /* remove opening and closing quotes */ char path[MAX_STR_SIZE]; snprintf(path, sizeof(path), "%s", &argv[1][1]); int path_len = strlen(path) - 1; path[path_len] = '\0'; if (path_len >= MAX_STR_SIZE) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit."); return; } FILE *file_to_send = fopen(path, "r"); if (file_to_send == NULL) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found."); return; } off_t filesize = file_size(path); if (filesize == -1) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File corrupt."); fclose(file_to_send); return; } char filename[MAX_STR_SIZE] = {0}; get_file_name(filename, sizeof(filename), path); int namelen = strlen(filename); int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, namelen); if (filenum == -1) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error sending file."); fclose(file_to_send); return; } int i; for (i = 0; i < MAX_FILES; ++i) { if (!file_senders[i].active) { memcpy(file_senders[i].filename, filename, namelen + 1); file_senders[i].active = true; file_senders[i].toxwin = self; file_senders[i].file = file_to_send; file_senders[i].filenum = filenum; file_senders[i].friendnum = self->num; file_senders[i].timestamp = get_unix_time(); file_senders[i].size = filesize; file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, tox_file_data_size(m, self->num), file_to_send); char sizestr[32]; bytes_convert_str(sizestr, sizeof(sizestr), filesize); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, filename, sizestr); ++num_active_file_senders; if (i == max_file_senders_index) ++max_file_senders_index; reset_file_sender_queue(); return; } } }
static void friendlist_onDraw(ToxWindow *self, Tox *m) { curs_set(0); werase(self->window); int x2, y2; getmaxyx(self->window, y2, x2); bool fix_statuses = x2 != self->x; /* true if window max x value has changed */ wattron(self->window, COLOR_PAIR(CYAN)); wprintw(self->window, " Press the"); wattron(self->window, A_BOLD); wprintw(self->window, " h "); wattroff(self->window, A_BOLD); wprintw(self->window, "key for help\n\n"); wattroff(self->window, COLOR_PAIR(CYAN)); if (blocklist_view == 1) { blocklist_onDraw(self, m, y2, x2); return; } uint64_t cur_time = get_unix_time(); struct tm cur_loc_tm = *localtime((const time_t *) &cur_time); pthread_mutex_lock(&Winthread.lock); int nf = tox_get_num_online_friends(m); pthread_mutex_unlock(&Winthread.lock); wattron(self->window, A_BOLD); wprintw(self->window, " Online: "); wattroff(self->window, A_BOLD); wprintw(self->window, "%d/%d \n\n", nf, Friends.num_friends); if ((y2 - FLIST_OFST) <= 0) return; int selected_num = 0; /* Determine which portion of friendlist to draw based on current position */ int page = Friends.num_selected / (y2 - FLIST_OFST); int start = (y2 - FLIST_OFST) * page; int end = y2 - FLIST_OFST + start; int i; for (i = start; i < Friends.num_friends && i < end; ++i) { int f = Friends.index[i]; bool f_selected = false; if (Friends.list[f].active) { if (i == Friends.num_selected) { wattron(self->window, A_BOLD); wprintw(self->window, " > "); wattroff(self->window, A_BOLD); selected_num = f; f_selected = true; } else { wprintw(self->window, " "); } if (Friends.list[f].online) { uint8_t status = Friends.list[f].status; int colour = WHITE; switch (status) { case TOX_USERSTATUS_NONE: colour = GREEN; break; case TOX_USERSTATUS_AWAY: colour = YELLOW; break; case TOX_USERSTATUS_BUSY: colour = RED; break; case TOX_USERSTATUS_INVALID: colour = MAGENTA; break; } wattron(self->window, COLOR_PAIR(colour) | A_BOLD); wprintw(self->window, "%s ", ONLINE_CHAR); wattroff(self->window, COLOR_PAIR(colour) | A_BOLD); if (f_selected) wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, A_BOLD); wprintw(self->window, "%s", Friends.list[f].name); wattroff(self->window, A_BOLD); if (f_selected) wattroff(self->window, COLOR_PAIR(BLUE)); /* Reset Friends.list[f].statusmsg on window resize */ if (fix_statuses) { char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH]; pthread_mutex_lock(&Winthread.lock); int s_len = tox_get_status_message(m, Friends.list[f].num, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH); pthread_mutex_unlock(&Winthread.lock); filter_str(statusmsg, s_len); snprintf(Friends.list[f].statusmsg, sizeof(Friends.list[f].statusmsg), "%s", statusmsg); Friends.list[f].statusmsg_len = strlen(Friends.list[f].statusmsg); } /* Truncate note if it doesn't fit on one line */ uint16_t maxlen = x2 - getcurx(self->window) - 2; if (Friends.list[f].statusmsg_len > maxlen) { Friends.list[f].statusmsg[maxlen - 3] = '\0'; strcat(Friends.list[f].statusmsg, "..."); Friends.list[f].statusmsg[maxlen] = '\0'; Friends.list[f].statusmsg_len = maxlen; } if (Friends.list[f].statusmsg[0]) wprintw(self->window, " %s", Friends.list[f].statusmsg); wprintw(self->window, "\n"); } else { wprintw(self->window, "%s ", OFFLINE_CHAR); if (f_selected) wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, A_BOLD); wprintw(self->window, "%s", Friends.list[f].name); wattroff(self->window, A_BOLD); if (f_selected) wattroff(self->window, COLOR_PAIR(BLUE)); uint64_t last_seen = Friends.list[f].last_online.last_on; if (last_seen != 0) { int day_dist = (cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday) % 365; const char *hourmin = Friends.list[f].last_online.hour_min_str; switch (day_dist) { case 0: wprintw(self->window, " Last seen: Today %s\n", hourmin); break; case 1: wprintw(self->window, " Last seen: Yesterday %s\n", hourmin); break; default: wprintw(self->window, " Last seen: %d days ago\n", day_dist); break; } } else { wprintw(self->window, " Last seen: Never\n"); } } } } self->x = x2; if (Friends.num_friends) { wmove(self->window, y2 - 1, 1); wattron(self->window, A_BOLD); wprintw(self->window, "Key: "); wattroff(self->window, A_BOLD); int i; for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i) wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff); } wrefresh(self->window); draw_del_popup(); if (self->help->active) help_onDraw(self); }
int main(int argc, char *argv[]) { init_signal_catchers(); parse_args(argc, argv); /* Make sure all written files are read/writeable only by the current user. */ umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); int config_err = init_data_files(); /* init user_settings struct and load settings from conf file */ user_settings_ = calloc(1, sizeof(struct user_settings)); if (user_settings_ == NULL) exit_toxic_err("failed in main", FATALERR_MEMORY); char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; int settings_err = settings_load(user_settings_, p); Tox *m = init_tox(); init_term(); /* enable stderr for debugging */ if (!arg_opts.debug) freopen("/dev/null", "w", stderr); if (m == NULL) exit_toxic_err("failed in main", FATALERR_NETWORKINIT); if (!arg_opts.ignore_data_file) load_data(m, DATA_FILE); prompt = init_windows(m); prompt_init_statusbar(prompt, m); /* thread for ncurses stuff */ if (pthread_mutex_init(&Winthread.lock, NULL) != 0) exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); #ifdef _AUDIO av = init_audio(prompt, m); set_primary_device(input, user_settings_->audio_in_dev); set_primary_device(output, user_settings_->audio_out_dev); #elif _SOUND_NOTIFY if ( init_devices() == de_InternalError ) line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); #endif /* _AUDIO */ init_notify(60, 3000); #ifdef _SOUND_NOTIFY // sound_notify(prompt, self_log_in, 0, NULL); #endif /* _SOUND_NOTIFY */ const char *msg; if (config_err) { msg = "Unable to determine configuration directory. Defaulting to 'data' for data file..."; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); } if (settings_err == -1) line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to load user settings"); if (arg_opts.use_proxy && !arg_opts.force_tcp) { msg = "* WARNING: Using a proxy without disabling UDP may leak your real IP address."; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "%s", msg); msg = " Use the -t option to disable UDP."; line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "%s", msg); } uint64_t last_save = (uint64_t) time(NULL); uint64_t looptimer = last_save; useconds_t msleepval = 40000; 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)) { pthread_mutex_lock(&Winthread.lock); store_data(m, DATA_FILE); pthread_mutex_unlock(&Winthread.lock); last_save = cur_time; } msleepval = optimal_msleepval(&looptimer, &loopcount, cur_time, msleepval); usleep(msleepval); } return 0; }
void line_info_print(ToxWindow *self) { ChatContext *ctx = self->chatwin; if (ctx == NULL) return; struct history *hst = ctx->hst; /* Only allow one new item to be added to chat window per call to this function */ line_info_check_queue(self); WINDOW *win = ctx->history; wclear(win); int y2, x2; getmaxyx(self->window, y2, x2); if (x2 <= SIDEBAR_WIDTH) return; if (self->is_groupchat) wmove(win, 0, 0); else wmove(win, 2, 0); struct line_info *line = hst->line_start->next; int numlines = 0; while (line && numlines++ <= y2) { uint8_t type = line->type; switch (type) { case OUT_MSG: /* fallthrough */ case OUT_MSG_READ: /* fallthrough */ case IN_MSG: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); int nameclr = GREEN; if (line->colour) nameclr = line->colour; else if (type == IN_MSG) nameclr = CYAN; wattron(win, COLOR_PAIR(nameclr)); wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); wattroff(win, COLOR_PAIR(nameclr)); if (line->msg[0] == '>') wattron(win, COLOR_PAIR(GREEN)); wprintw(win, "%s", line->msg); if (line->msg[0] == '>') wattroff(win, COLOR_PAIR(GREEN)); if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) { wattron(win, COLOR_PAIR(RED)); wprintw(win, " x", line->msg); wattroff(win, COLOR_PAIR(RED)); if (line->noread_flag == false) { line->noread_flag = true; line->len += 2; } } wprintw(win, "\n", line->msg); break; case OUT_ACTION_READ: /* fallthrough */ case OUT_ACTION: /* fallthrough */ case IN_ACTION: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(YELLOW)); 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)) { wattron(win, COLOR_PAIR(RED)); wprintw(win, " x", line->msg); wattroff(win, COLOR_PAIR(RED)); if (line->noread_flag == false) { line->noread_flag = true; line->len += 2; } } wprintw(win, "\n", line->msg); break; case SYS_MSG: if (line->timestr[0]) { wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); } if (line->bold) wattron(win, A_BOLD); if (line->colour) wattron(win, COLOR_PAIR(line->colour)); wprintw(win, "%s\n", line->msg); if (line->bold) wattroff(win, A_BOLD); if (line->colour) wattroff(win, COLOR_PAIR(line->colour)); break; case PROMPT: wattron(win, COLOR_PAIR(GREEN)); wprintw(win, "$ "); wattroff(win, COLOR_PAIR(GREEN)); if (line->msg[0]) wprintw(win, "%s", line->msg); wprintw(win, "\n"); break; case CONNECTION: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(line->colour)); wprintw(win, "%s ", user_settings->line_join); wattron(win, A_BOLD); wprintw(win, "%s ", line->name1); wattroff(win, A_BOLD); wprintw(win, "%s\n", line->msg); wattroff(win, COLOR_PAIR(line->colour)); break; case DISCONNECTION: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(line->colour)); wprintw(win, "%s ", user_settings->line_quit); wattron(win, A_BOLD); wprintw(win, "%s ", line->name1); wattroff(win, A_BOLD); wprintw(win, "%s\n", line->msg); wattroff(win, COLOR_PAIR(line->colour)); break; case NAME_CHANGE: wattron(win, COLOR_PAIR(BLUE)); wprintw(win, "%s ", line->timestr); wattroff(win, COLOR_PAIR(BLUE)); wattron(win, COLOR_PAIR(MAGENTA)); wprintw(win, "%s ", user_settings->line_alert); wattron(win, A_BOLD); wprintw(win, "%s", line->name1); wattroff(win, A_BOLD); wprintw(win, "%s", line->msg); wattron(win, A_BOLD); wprintw(win, "%s\n", line->name2); wattroff(win, A_BOLD); wattroff(win, COLOR_PAIR(MAGENTA)); break; } line = line->next; } /* keep calling until queue is empty */ if (hst->queue_sz > 0) line_info_print(self); }