aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrumeet <yuuta@yuuta.moe>2022-01-07 15:00:40 -0800
committerTrumeet <yuuta@yuuta.moe>2022-01-07 15:00:40 -0800
commite1887b523a1401c357ae2440bbb5eb96037a5c98 (patch)
treec8f0137234f7b8cadd9ef1be6d26dc9550e77309
downloadgists-e1887b523a1401c357ae2440bbb5eb96037a5c98.tar
gists-e1887b523a1401c357ae2440bbb5eb96037a5c98.tar.gz
gists-e1887b523a1401c357ae2440bbb5eb96037a5c98.tar.bz2
gists-e1887b523a1401c357ae2440bbb5eb96037a5c98.zip
First Commit, add Monitor
-rw-r--r--Monitor/CMakeLists.txt20
-rw-r--r--Monitor/README.md1
-rw-r--r--Monitor/main.c491
-rw-r--r--README.md3
4 files changed, 515 insertions, 0 deletions
diff --git a/Monitor/CMakeLists.txt b/Monitor/CMakeLists.txt
new file mode 100644
index 0000000..ed5004a
--- /dev/null
+++ b/Monitor/CMakeLists.txt
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.21)
+project(Monitor C CXX)
+
+set(CMAKE_C_STANDARD 99)
+IF (CMAKE_BUILD_TYPE MATCHES Debug)
+ add_compile_definitions(TD_USE_ASAN)
+ENDIF (CMAKE_BUILD_TYPE MATCHES Debug)
+add_subdirectory(td)
+
+set(CMAKE_C_FLAGS_DEBUG
+ "${CMAKE_C_FLAGS_DEBUG} -g3 -O0 -fsanitize=address")
+set(CMAKE_EXE_LINKER_FLAGS_DEBUG
+ "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address")
+add_definitions(-D_POSIX_C_SOURCE=200809L)
+
+add_executable(Monitor
+ main.c
+ )
+target_include_directories(Monitor PUBLIC "${PROJECT_BINARY_DIR}")
+target_link_libraries(Monitor PRIVATE tdc)
diff --git a/Monitor/README.md b/Monitor/README.md
new file mode 100644
index 0000000..d0167df
--- /dev/null
+++ b/Monitor/README.md
@@ -0,0 +1 @@
+Monitor online / offline of a specific Telegram user. Used to help test one of my friend's userbot.
diff --git a/Monitor/main.c b/Monitor/main.c
new file mode 100644
index 0000000..a9036e2
--- /dev/null
+++ b/Monitor/main.c
@@ -0,0 +1,491 @@
+#include <stdio.h>
+#include <td/telegram/td_c_client.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <termios.h>
+#include <stdbool.h>
+#include <assert.h>
+
+struct td_callback {
+ struct td_callback *next;
+
+ long long request_id;
+
+ void (*cb)(bool, struct TdObject *, struct TdError *);
+};
+
+int td = -1;
+long long my_id = -1;
+static struct td_callback *cbs;
+
+/**
+ * Last request_id. Increased whenever sending a query.
+ *
+ * Write: any
+ * Read: any
+ */
+static atomic_llong last_req_id = 0;
+
+/**
+ * Set to true by main thread when received authorizationStateClosed or authorizationStateClosing.
+ * Stop accepting new queries.
+ *
+ * Write: main
+ * Read: any
+ */
+bool closing = false;
+
+static bool sighandler_setup = false;
+static pthread_t thread_sighandler;
+
+static void tg_close();
+
+static int td_send(void *func, void (*cb)(bool, struct TdObject *, struct TdError *));
+
+static void fetal_cb(bool successful, struct TdObject *result, struct TdError *error);
+
+/**
+ * Used for sigwait(2).
+ */
+sigset_t set;
+
+static int read_input(const char *prompt, char **buf, bool secure) {
+ printf("%s", prompt);
+ struct termios oldt, newt;
+ if (secure) {
+ tcgetattr(0, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ECHO);
+ tcsetattr(0, TCSANOW, &newt);
+ }
+ size_t len = 0;
+ ssize_t read = getline(buf, &len, stdin);
+ if (secure) {
+ printf("\n"); /* Manually insert a new line */
+ tcsetattr(0, TCSANOW, &oldt);
+ }
+ if (read == -1) {
+ int r = errno;
+ fprintf(stderr, "Cannot read input: %s\n", strerror(r));
+ return r;
+ }
+ (*buf)[read - 1] = '\0'; /* Remove \n */
+ return 0;
+}
+
+static void *main_sighandler(void *arg) {
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ int r;
+ int sig;
+
+ while (true) {
+ r = sigwait(&set, &sig);
+ if (r) {
+ fprintf(stderr, "Cannot call sigwait(): %d.\n", r);
+ goto cleanup;
+ }
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ if (td == -1) goto cleanup;
+ tg_close();
+ goto cleanup;
+ default:
+ break;
+ }
+ }
+ cleanup:
+ pthread_exit(NULL);
+}
+
+static int
+tdcb_push(long long request_id, void (*cb)(bool, struct TdObject *, struct TdError *)) {
+ struct td_callback *current_ptr = malloc(sizeof(struct td_callback));
+ if (current_ptr == NULL) {
+ int r = errno;
+ fprintf(stderr, "Cannot allocate memory: %s\n", strerror(r));
+ return r;
+ }
+ current_ptr->next = NULL;
+ current_ptr->request_id = request_id;
+ current_ptr->cb = cb;
+ if (cbs == NULL) {
+ cbs = current_ptr;
+ } else {
+ current_ptr->next = cbs;
+ cbs = current_ptr;
+ }
+ return 0;
+}
+
+static int td_send(void *func, void (*cb)(bool, struct TdObject *, struct TdError *)) {
+ if (closing) {
+ TdDestroyObjectFunction((struct TdFunction *) func);
+ return 0;
+ }
+ if (last_req_id == LLONG_MAX) last_req_id = 0;
+ last_req_id++;
+ int r;
+ if (cb != NULL && (r = tdcb_push(last_req_id, cb))) {
+ return r;
+ }
+ TdCClientSend(td, (struct TdRequest) {
+ last_req_id,
+ (struct TdFunction *) func
+ });
+ return 0;
+}
+
+static void tdcb_call(long long request_id, bool successful, struct TdObject *result, struct TdError *error) {
+ if (cbs == NULL) return;
+ struct td_callback *current_ptr = cbs;
+ bool node_found = false;
+ while (current_ptr != NULL) {
+ if (current_ptr->request_id == request_id) {
+ node_found = true;
+ current_ptr->cb(successful, result, error);
+ if (error != NULL &&
+ current_ptr->cb == &fetal_cb) {
+ /* The fetal_cb callback does not print anything */
+ /* because it does not know the request_id. */
+ fprintf(stderr, "Error: Request %lld: %s (%d)\n",
+ request_id,
+ error->message_,
+ error->code_);
+ }
+ break;
+ }
+ current_ptr = current_ptr->next;
+ }
+ if (node_found) {
+ /*
+ * The callback function may insert nodes to the link.
+ * Therefore, we do the iteration again after calling the function to
+ * delete the current one.
+ * Need a better implementation.
+ */
+ current_ptr = cbs;
+ struct td_callback *prev_ptr = NULL;
+ while (current_ptr != NULL) {
+ if (current_ptr->request_id == request_id) {
+ if (prev_ptr == NULL) cbs = current_ptr->next;
+ else prev_ptr->next = current_ptr->next;
+ free(current_ptr);
+ return;
+ }
+ prev_ptr = current_ptr;
+ current_ptr = current_ptr->next;
+ }
+ }
+ if (error != NULL) {
+ /* No callback found, Display default error message. */
+ fprintf(stderr, "Error: Request %lld: %s (%d)\n",
+ request_id,
+ error->message_,
+ error->code_);
+ }
+}
+
+static void tdcb_free() {
+ struct td_callback *current_ptr = cbs;
+ while (current_ptr != NULL) {
+ struct td_callback *bak = current_ptr;
+ current_ptr = current_ptr->next;
+ free(bak);
+ }
+ cbs = NULL;
+}
+
+static void tg_close() {
+ if (td == -1) return;
+ td_send(TdCreateObjectClose(), NULL);
+}
+
+static void fetal_cb(bool successful, struct TdObject *result, struct TdError *error) {
+ if (!successful) {
+ tg_close();
+ }
+}
+
+static void auth_phone(bool successful, struct TdObject *result, struct TdError *error) {
+ if (result) TdDestroyObjectObject(result);
+ if (closing || successful) return;
+ if (error != NULL) { /* error == NULL when caused from update handler */
+ fprintf(stderr, "Invalid phone number: %s (%d)\n",
+ error->message_,
+ error->code_);
+ }
+ char *phone = NULL;
+ if (read_input("Phone number: ", &phone, false)) {
+ tg_close();
+ return;
+ }
+ td_send(TdCreateObjectSetAuthenticationPhoneNumber(phone,
+ TdCreateObjectPhoneNumberAuthenticationSettings(
+ false, false, false, false,
+ (struct TdVectorString *) TdCreateObjectVectorObject(0,
+ NULL))),
+ &auth_phone);
+ free(phone);
+}
+
+static void auth_code(bool successful, struct TdObject *result, struct TdError *error) {
+ if (result) TdDestroyObjectObject(result);
+ if (closing || successful) return;
+ if (error != NULL) { /* error == NULL when caused from update handler */
+ fprintf(stderr, "Invalid verification code: %s (%d)\n",
+ error->message_,
+ error->code_);
+ }
+ char *code = NULL;
+ if (read_input("Verification code: ", &code, false)) {
+ tg_close();
+ return;
+ }
+ td_send(TdCreateObjectCheckAuthenticationCode(code), &auth_code);
+ free(code);
+}
+
+static void auth_pass(bool successful, struct TdObject *result, struct TdError *error) {
+ if (closing || successful) return;
+ if (error != NULL) { /* error == NULL when caused from update handler */
+ fprintf(stderr, "Invalid password: %s (%d)\n",
+ error->message_,
+ error->code_);
+ }
+ printf("Password ");
+ if (result != NULL && result->ID == CODE_AuthorizationStateWaitPassword) {
+ struct TdAuthorizationStateWaitPassword *waitPassword =
+ (struct TdAuthorizationStateWaitPassword *) result;
+ printf("(Hint: %s)", waitPassword->password_hint_);
+ }
+ /* The result is owned by the main loop -- It will be freed there. */
+ printf(": ");
+ char *password = NULL;
+ if (read_input("", &password, true)) {
+ tg_close();
+ return;
+ }
+ td_send(TdCreateObjectCheckAuthenticationPassword(password), &auth_pass);
+ free(password);
+}
+
+static void handle_auth(const struct TdUpdateAuthorizationState *update) {
+ switch (update->authorization_state_->ID) {
+ case CODE_AuthorizationStateWaitTdlibParameters:
+ td_send(TdCreateObjectSetTdlibParameters(TdCreateObjectTdlibParameters(
+ false,
+ "./td/",
+ NULL,
+ false,
+ false,
+ false,
+ false,
+ 72527,
+ "ba974de6a156b1bf310286ceff85b3e6",
+ "en",
+ "Desktop",
+ "0.0",
+ "Test",
+ false,
+ true
+ )),
+ &fetal_cb);
+ break;
+ case CODE_AuthorizationStateWaitPhoneNumber: {
+ auth_phone(false, NULL, NULL);
+ break;
+ }
+ case CODE_AuthorizationStateWaitCode: {
+ auth_code(false, NULL, NULL);
+ break;
+ }
+ case CODE_AuthorizationStateWaitPassword: {
+ auth_pass(false,
+ (struct TdObject *) update->authorization_state_, NULL);
+ break;
+ }
+ case CODE_AuthorizationStateWaitEncryptionKey: {
+ td_send(TdCreateObjectCheckDatabaseEncryptionKey(TdCreateObjectBytes((unsigned char *) {0x0}, 0)),
+ &fetal_cb);
+ break;
+ }
+ case CODE_AuthorizationStateReady: {
+ printf("System operational.\n");
+ break;
+ }
+ /* Closed state is handled in the main loop. */
+ case CODE_AuthorizationStateLoggingOut:
+ case CODE_AuthorizationStateClosing: {
+ closing = true;
+ break;
+ }
+ case CODE_AuthorizationStateWaitOtherDeviceConfirmation: {
+ struct TdAuthorizationStateWaitOtherDeviceConfirmation *waitOtherDeviceConfirmation =
+ (struct TdAuthorizationStateWaitOtherDeviceConfirmation *) update->authorization_state_;
+ printf("Please scan the QR code of the following link using another Telegram seession:\n%s\n",
+ waitOtherDeviceConfirmation->link_);
+ break;
+ }
+ default: {
+ fprintf(stderr, "Unsupported authorization state: %d. Aborted.\n",
+ update->authorization_state_->ID);
+ tg_close();
+ break;
+ }
+ }
+}
+
+static void handle_update(const struct TdUpdate *update) {
+ switch (update->ID) {
+ case CODE_UpdateAuthorizationState:
+ handle_auth((struct TdUpdateAuthorizationState *) update);
+ break;
+ case CODE_UpdateUserStatus: {
+ struct TdUpdateUserStatus *u =
+ (struct TdUpdateUserStatus *) update;
+ if (u->user_id_ != TARGET_UID) {
+ break;
+ }
+ char buff[20];
+ time_t now = time(NULL);
+ strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));
+ char *type = NULL;
+ char t1[40];
+ switch (u->status_->ID) {
+ case CODE_UserStatusEmpty:
+ type = "empty";
+ break;
+ case CODE_UserStatusOnline: {
+ struct TdUserStatusOnline *s =
+ (struct TdUserStatusOnline *) u->status_;
+ sprintf(t1, "online (expires %lu seconds later)",
+ s->expires_ - ((unsigned long) time(NULL)));
+ type = t1;
+ break;
+ }
+ case CODE_UserStatusLastMonth:
+ type = "offline (seen last month)";
+ break;
+ case CODE_UserStatusLastWeek:
+ type = "offline (seen last week)";
+ break;
+ case CODE_UserStatusOffline: {
+ struct TdUserStatusOffline *s =
+ (struct TdUserStatusOffline *) u->status_;
+ sprintf(t1, "offline (last seen %d)",
+ s->was_online_);
+ type = t1;
+ break;
+ }
+ default: {
+ type = "unknown";
+ break;
+ }
+ }
+ assert(type);
+ printf("%s %lld is %s.\n",
+ buff,
+ u->user_id_,
+ type);
+ break;
+ }
+ case CODE_UpdateConnectionState: {
+ struct TdUpdateConnectionState *u =
+ (struct TdUpdateConnectionState *) update;
+ switch (u->state_->ID) {
+ case CODE_ConnectionStateConnecting:
+ printf("Connecting ...\n");
+ break;
+ case CODE_ConnectionStateConnectingToProxy:
+ printf("Connecting to proxy ...\n");
+ break;
+ case CODE_ConnectionStateReady:
+ printf("Connected.\n");
+ break;
+ case CODE_ConnectionStateUpdating:
+ printf("Updating ...\n");
+ break;
+ case CODE_ConnectionStateWaitingForNetwork:
+ printf("Waiting for network ...\n");
+ break;
+ default:
+ printf("Unknown connection state.\n");
+ break;
+ }
+ break;
+ }
+ }
+}
+
+int main(void) {
+ int r;
+ sigemptyset(&set);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGUSR1);
+ r = pthread_sigmask(SIG_BLOCK, &set, NULL);
+ if (r) {
+ fprintf(stderr, "Cannot call pthread_sigmask(): %d\n", r);
+ return r;
+ }
+ r = pthread_create(&thread_sighandler, NULL, &main_sighandler, NULL);
+ if (r) {
+ fprintf(stderr, "Cannot call pthread_create(): %d\n", r);
+ return r;
+ }
+ sighandler_setup = true;
+ td = TdCClientCreateId();
+ TdDestroyObjectObject(TdCClientExecute((struct TdFunction *) TdCreateObjectSetLogVerbosityLevel(0)));
+ td_send(TdCreateObjectGetOption("version"), &fetal_cb);
+
+ struct TdResponse response;
+ while (1) {
+ response = TdCClientReceive(5);
+ struct TdObject *obj = response.object;
+ if (obj == NULL) continue;
+ const bool is_update = response.request_id == 0;
+ if (is_update &&
+ obj->ID == CODE_UpdateAuthorizationState &&
+ ((struct TdUpdate *) obj)->ID == CODE_UpdateAuthorizationState &&
+ ((struct TdUpdateAuthorizationState *) obj)->authorization_state_->ID ==
+ CODE_AuthorizationStateClosed) {
+ closing = true;
+ TdDestroyObjectObject(obj);
+ goto cleanup;
+ }
+ if (is_update) {
+ handle_update((struct TdUpdate *) obj);
+ TdDestroyObjectObject(obj);
+ continue;
+ }
+ switch (obj->ID) {
+ case CODE_Ok:
+ tdcb_call(response.request_id, true, NULL, NULL);
+ break;
+ case CODE_Error: {
+ struct TdError *error =
+ (struct TdError *) obj;
+ tdcb_call(response.request_id, false, NULL, error);
+ break;
+ }
+ default:
+ tdcb_call(response.request_id, true, obj, NULL);
+ break;
+ }
+ TdDestroyObjectObject(obj);
+ }
+ cleanup:
+ if (sighandler_setup) {
+ pthread_cancel(thread_sighandler);
+ r = pthread_join(thread_sighandler, NULL);
+ if (!r) sighandler_setup = false;
+ }
+ tdcb_free();
+ return 0;
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2549393
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Gists
+
+Small temp projects