/* This is a CLI program and a little testing frame. This file must not be included when using Delta Chat Core as a library. Usage: messenger-backend (for "Code::Blocks, use Project / Set programs' arguments") all further options can be set using the set-command (type ? for help). */ #include #include #include "../src/deltachat.h" #include "../src/dc_context.h" #include "cmdline.h" #include "stress.h" /******************************************************************************* * Event Handler ******************************************************************************/ static int s_do_log_info = 1; #define ANSI_RED "\e[31m" #define ANSI_YELLOW "\e[33m" #define ANSI_NORMAL "\e[0m" static uintptr_t receive_event(dc_context_t* context, int event, uintptr_t data1, uintptr_t data2) { switch (event) { case DC_EVENT_GET_STRING: break; /* do not show the event as this would fill the screen */ case DC_EVENT_INFO: if (s_do_log_info) { printf("%s\n", (char*)data2); } break; case DC_EVENT_SMTP_CONNECTED: printf("[DC_EVENT_SMTP_CONNECTED] %s\n", (char*)data2); break; case DC_EVENT_IMAP_CONNECTED: printf("[DC_EVENT_IMAP_CONNECTED] %s\n", (char*)data2); break; case DC_EVENT_SMTP_MESSAGE_SENT: printf("[DC_EVENT_SMTP_MESSAGE_SENT] %s\n", (char*)data2); break; case DC_EVENT_WARNING: printf("[Warning] %s\n", (char*)data2); break; case DC_EVENT_ERROR: printf(ANSI_RED "[DC_EVENT_ERROR] %s" ANSI_NORMAL "\n", (char*)data2); break; case DC_EVENT_ERROR_NETWORK: printf(ANSI_RED "[DC_EVENT_ERROR_NETWORK] first=%i, msg=%s" ANSI_NORMAL "\n", (int)data1, (char*)data2); break; case DC_EVENT_ERROR_SELF_NOT_IN_GROUP: printf(ANSI_RED "[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] %s" ANSI_NORMAL "\n", (char*)data2); break; case DC_EVENT_HTTP_GET: case DC_EVENT_HTTP_POST: { char* url = dc_strdup((char*)data1); char* param = strchr(url, '?'); if (param) { *param = 0; param++; } else { param = ""; } char* ret = NULL; char* tempFile = dc_get_fine_pathNfilename(context, context->blobdir, "curl.result"); char* cmd = event==DC_EVENT_HTTP_GET? dc_mprintf("curl --silent --location --fail --insecure %s%s%s > %s", url, param[0]? "?" : "", param, tempFile) : dc_mprintf("curl --silent -d \"%s\" %s > %s", param, url, tempFile); int error = system(cmd); if (error == 0) { /* -1=system() error, !0=curl errors forced by -f, 0=curl success */ size_t bytes = 0; dc_read_file(context, tempFile, (void**)&ret, &bytes); } free(cmd); free(tempFile); free(url); return (uintptr_t)ret; } case DC_EVENT_IS_OFFLINE: printf(ANSI_YELLOW "{{Received DC_EVENT_IS_OFFLINE()}}\n" ANSI_NORMAL); break; case DC_EVENT_MSGS_CHANGED: printf(ANSI_YELLOW "{{Received DC_EVENT_MSGS_CHANGED(%i, %i)}}\n" ANSI_NORMAL, (int)data1, (int)data2); break; case DC_EVENT_CONTACTS_CHANGED: printf(ANSI_YELLOW "{{Received DC_EVENT_CONTACTS_CHANGED()}}\n" ANSI_NORMAL); break; case DC_EVENT_LOCATION_CHANGED: printf(ANSI_YELLOW "{{Received DC_EVENT_LOCATION_CHANGED(contact=%i)}}\n" ANSI_NORMAL, (int)data1); break; case DC_EVENT_CONFIGURE_PROGRESS: printf(ANSI_YELLOW "{{Received DC_EVENT_CONFIGURE_PROGRESS(%i ‰)}}\n" ANSI_NORMAL, (int)data1); break; case DC_EVENT_IMEX_PROGRESS: printf(ANSI_YELLOW "{{Received DC_EVENT_IMEX_PROGRESS(%i ‰)}}\n" ANSI_NORMAL, (int)data1); break; case DC_EVENT_IMEX_FILE_WRITTEN: printf(ANSI_YELLOW "{{Received DC_EVENT_IMEX_FILE_WRITTEN(%s)}}\n" ANSI_NORMAL, (char*)data1); break; case DC_EVENT_FILE_COPIED: printf(ANSI_YELLOW "{{Received DC_EVENT_FILE_COPIED(%s)}}\n" ANSI_NORMAL, (char*)data1); break; case DC_EVENT_CHAT_MODIFIED: printf(ANSI_YELLOW "{{Received DC_EVENT_CHAT_MODIFIED(%i)}}\n" ANSI_NORMAL, (int)data1); break; default: printf(ANSI_YELLOW "{{Received DC_EVENT_%i(%i, %i)}}\n" ANSI_NORMAL, (int)event, (int)data1, (int)data2); break; } return 0; } /******************************************************************************* * Threads for waiting for messages and for jobs ******************************************************************************/ static pthread_t inbox_thread = 0; static int run_threads = 0; static void* inbox_thread_entry_point (void* entry_arg) { dc_context_t* context = (dc_context_t*)entry_arg; while (run_threads) { // jobs(), fetch() and idle() MUST be called from the same single thread // and MUST be called sequentially. dc_perform_imap_jobs(context); dc_perform_imap_fetch(context); if (run_threads) { dc_perform_imap_idle(context); // this may take hours ... } } return NULL; } static pthread_t mvbox_thread = 0; static void* mvbox_thread_entry_point (void* entry_arg) { dc_context_t* context = (dc_context_t*)entry_arg; while (run_threads) { // fetch() and idle() MUST be called from the same single thread // and MUST be called sequentially. dc_perform_mvbox_fetch(context); if (run_threads) { dc_perform_mvbox_idle(context); // this may take hours ... } } return NULL; } static pthread_t sentbox_thread = 0; static void* sentbox_thread_entry_point (void* entry_arg) { dc_context_t* context = (dc_context_t*)entry_arg; while (run_threads) { // fetch() and idle() MUST be called from the same single thread // and MUST be called sequentially. dc_perform_sentbox_fetch(context); if (run_threads) { dc_perform_sentbox_idle(context); // this may take hours ... } } return NULL; } static pthread_t smtp_thread = 0; static void* smtp_thread_entry_point (void* entry_arg) { dc_context_t* context = (dc_context_t*)entry_arg; while (run_threads) { // jobs() and idle() MUST be called from the same single thread // and MUST be called sequentially. dc_perform_smtp_jobs(context); if (run_threads) { dc_perform_smtp_idle(context); // this may take hours ... } } return NULL; } static void start_threads(dc_context_t* context) { run_threads = 1; if (!inbox_thread) { pthread_create(&inbox_thread, NULL, inbox_thread_entry_point, context); } if (!mvbox_thread) { pthread_create(&mvbox_thread, NULL, mvbox_thread_entry_point, context); } if (!sentbox_thread) { pthread_create(&sentbox_thread, NULL, sentbox_thread_entry_point, context); } if (!smtp_thread) { pthread_create(&smtp_thread, NULL, smtp_thread_entry_point, context); } } static void stop_threads(dc_context_t* context) { run_threads = 0; dc_interrupt_imap_idle(context); dc_interrupt_mvbox_idle(context); dc_interrupt_sentbox_idle(context); dc_interrupt_smtp_idle(context); pthread_join(inbox_thread, NULL); pthread_join(mvbox_thread, NULL); pthread_join(sentbox_thread, NULL); pthread_join(smtp_thread, NULL); inbox_thread = 0; mvbox_thread = 0; sentbox_thread = 0; smtp_thread = 0; } /******************************************************************************* * The main loop ******************************************************************************/ static char* read_cmd(void) { printf("> "); static char cmdbuffer[1024]; fgets(cmdbuffer, 1000, stdin); while (strlen(cmdbuffer)>0 && (cmdbuffer[strlen(cmdbuffer)-1]=='\n' || cmdbuffer[strlen(cmdbuffer)-1]==' ')) { cmdbuffer[strlen(cmdbuffer)-1] = '\0'; } return cmdbuffer; } int main(int argc, char ** argv) { char* cmd = NULL; dc_context_t* context = dc_context_new(receive_event, NULL, "CLI"); int stresstest_only = 0; dc_cmdline_skip_auth(context); /* disable the need to enter the command `auth ` for all mailboxes. */ /* open database from the commandline (if omitted, it can be opened using the `open`-command) */ if (argc == 2) { if (strcmp(argv[1], "--stress")==0) { stresstest_only = 1; } else if (!dc_open(context, argv[1], NULL)) { printf("ERROR: Cannot open %s.\n", argv[1]); } } else if (argc != 1) { printf("ERROR: Bad arguments\n"); } s_do_log_info = 0; stress_functions(context); s_do_log_info = 1; if (stresstest_only) { return 0; } printf("Delta Chat Core is awaiting your commands.\n"); /* wait for command */ while (1) { /* read command */ const char* cmdline = read_cmd(); free(cmd); cmd = dc_strdup(cmdline); char* arg1 = strchr(cmd, ' '); if (arg1) { *arg1 = 0; arg1++; } if (strcmp(cmd, "connect")==0) { start_threads(context); } else if (strcmp(cmd, "disconnect")==0) { stop_threads(context); } else if (strcmp(cmd, "smtp-jobs")==0) { if (run_threads) { printf("smtp-jobs are already running in a thread.\n"); } else { dc_perform_smtp_jobs(context); } } else if (strcmp(cmd, "imap-jobs")==0) { if (run_threads) { printf("imap-jobs are already running in a thread.\n"); } else { dc_perform_imap_jobs(context); } } else if (strcmp(cmd, "configure")==0) { start_threads(context); dc_configure(context); } else if (strcmp(cmd, "oauth2")==0) { char* addr = dc_get_config(context, "addr"); if (addr==NULL || addr[0]==0) { printf("oauth2: set addr first.\n"); } else { char* oauth2_url = dc_get_oauth2_url(context, addr, "chat.delta:/com.b44t.messenger"); if (oauth2_url==NULL) { printf("OAuth2 not available for %s.\n", addr); } else { printf("Open the following url, " "set mail_pw to the generated token " "and server_flags to 2:\n%s\n", oauth2_url); } free(oauth2_url); } free(addr); } else if (strcmp(cmd, "clear")==0) { printf("\n\n\n\n"); /* insert some blank lines to visualize the break in the buffer */ printf("\e[1;1H\e[2J"); /* should work on ANSI terminals and on Windows 10. If not, well, then not. */ } else if (strcmp(cmd, "getqr")==0 || strcmp(cmd, "getbadqr")==0) { start_threads(context); char* qrstr = dc_get_securejoin_qr(context, arg1? atoi(arg1) : 0); if (qrstr && qrstr[0]) { if (strcmp(cmd, "getbadqr")==0 && strlen(qrstr)>40) { for (int i = 12; i < 22; i++) { qrstr[i] = '0'; } } printf("%s\n", qrstr); char* syscmd = dc_mprintf("qrencode -t ansiutf8 \"%s\" -o -", qrstr); /* `-t ansiutf8`=use back/write, `-t utf8`=use terminal colors */ system(syscmd); free(syscmd); } free(qrstr); } else if (strcmp(cmd, "joinqr")==0) { start_threads(context); if (arg1) { dc_join_securejoin(context, arg1); } } else if (strcmp(cmd, "exit")==0) { break; } else if (cmd[0] == 0) { ; /* nothing typed */ } else { char* execute_result = dc_cmdline(context, cmdline); if (execute_result) { printf("%s\n", execute_result); free(execute_result); } } } free(cmd); stop_threads(context); dc_close(context); dc_context_unref(context); context = NULL; return 0; }