switch over to use sessions
This commit is contained in:
		
							parent
							
								
									ffcc9f60f7
								
							
						
					
					
						commit
						30fb4a73df
					
				| @ -12,11 +12,13 @@ SET(SOURCES | ||||
|   "include/dc/api.h" | ||||
|   "include/dc/apisync.h" | ||||
|   "include/dc/channel.h" | ||||
|   "include/dc/event.h" | ||||
|   "include/dc/gateway.h" | ||||
|   "include/dc/guild.h" | ||||
|   "include/dc/loop.h" | ||||
|   "include/dc/message.h" | ||||
|   "include/dc/refable.h" | ||||
|   "include/dc/session.h" | ||||
|   "include/dc/util.h" | ||||
|   "src/account.c" | ||||
|   "src/api.c" | ||||
| @ -26,11 +28,13 @@ SET(SOURCES | ||||
|   "src/api-user.c" | ||||
|   "src/apisync.c" | ||||
|   "src/channel.c" | ||||
|   "src/event.c" | ||||
|   "src/gateway.c" | ||||
|   "src/guild.c" | ||||
|   "src/loop.c" | ||||
|   "src/message.c" | ||||
|   "src/refable.c" | ||||
|   "src/session.c" | ||||
|   "src/util.c" | ||||
|   "src/ws-frames.c" | ||||
|   ) | ||||
|  | ||||
							
								
								
									
										25
									
								
								libdc/include/dc/event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								libdc/include/dc/event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| #ifndef DC_EVENT_H | ||||
| #define DC_EVENT_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <jansson.h> | ||||
| 
 | ||||
| struct dc_event_; | ||||
| typedef struct dc_event_ *dc_event_t; | ||||
| 
 | ||||
| dc_event_t dc_event_new(char const *type, json_t *payload); | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns the event type. This is an upper case string, with | ||||
|  * words separated by underscores. For a list of available event | ||||
|  * types please see the Discord documentation. | ||||
|  */ | ||||
| char const *dc_event_type(dc_event_t e); | ||||
| 
 | ||||
| /**
 | ||||
|  * The JSON payload associated with the given event type. Note this | ||||
|  * could be json_null() if the event has no associated payload. | ||||
|  */ | ||||
| json_t *dc_event_payload(dc_event_t e); | ||||
| 
 | ||||
| #endif | ||||
| @ -5,12 +5,22 @@ | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <dc/account.h> | ||||
| 
 | ||||
| #include <curl/curl.h> | ||||
| #include <dc/event.h> | ||||
| 
 | ||||
| struct dc_gateway_; | ||||
| typedef struct dc_gateway_ *dc_gateway_t; | ||||
| 
 | ||||
| /**
 | ||||
|  * The event callback that will be called by the gateway when a new event | ||||
|  * arrives. First parameter is the gateway responsible for sending this | ||||
|  * event, second parameter is the event in question, third parameter is | ||||
|  * user defined callback data. | ||||
|  * | ||||
|  * Note that the event will be allocated, and dc_unref()'d by the gateway, | ||||
|  * so if you need the event around you need to dc_ref() it. | ||||
|  */ | ||||
| typedef void (*dc_gateway_event_callback_t)(dc_gateway_t, dc_event_t, void*); | ||||
| 
 | ||||
| typedef enum { | ||||
|     GATEWAY_OPCODE_EVENT = 0, | ||||
|     GATEWAY_OPCODE_PING = 1, | ||||
| @ -31,6 +41,13 @@ dc_gateway_t dc_gateway_new(void); | ||||
| 
 | ||||
| void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login); | ||||
| 
 | ||||
| void dc_gateway_set_callback(dc_gateway_t gw, dc_gateway_event_callback_t c, | ||||
|                              void *userdata); | ||||
| 
 | ||||
| /**
 | ||||
|  * Connect the given gateway. Does nothing if the gateway is already | ||||
|  * connected. | ||||
|  */ | ||||
| bool dc_gateway_connect(dc_gateway_t gw); | ||||
| 
 | ||||
| /**
 | ||||
| @ -45,7 +62,10 @@ void dc_gateway_disconnect(dc_gateway_t gw); | ||||
| bool dc_gateway_connected(dc_gateway_t gw); | ||||
| 
 | ||||
| /**
 | ||||
|  * Process the queue of data that came from the websocket. | ||||
|  * Process the queue of data that came from the websocket. Since the | ||||
|  * gateway handle is not part of whole event_base_loop() shebang, this | ||||
|  * must be called individually. dc_loop_once() will do this for you, if | ||||
|  * you opt to use dc_loop() (which you should). | ||||
|  */ | ||||
| void dc_gateway_process(dc_gateway_t gw); | ||||
| 
 | ||||
|  | ||||
| @ -40,11 +40,21 @@ struct event_base *dc_loop_event_base(dc_loop_t l); | ||||
|  */ | ||||
| void dc_loop_add_api(dc_loop_t loop, dc_api_t api); | ||||
| 
 | ||||
| /**
 | ||||
|  * Remove the given API handle from the loop. | ||||
|  */ | ||||
| void dc_loop_remove_api(dc_loop_t loop, dc_api_t api); | ||||
| 
 | ||||
| /**
 | ||||
|  * Add a gateway to be handled with the rest. | ||||
|  */ | ||||
| void dc_loop_add_gateway(dc_loop_t loop, dc_gateway_t gw); | ||||
| 
 | ||||
| /**
 | ||||
|  * Remove the given gateway from the loop. | ||||
|  */ | ||||
| void dc_loop_remove_gateway(dc_loop_t loop, dc_gateway_t gw); | ||||
| 
 | ||||
| /**
 | ||||
|  * Loop once, and process one message in the queues of the event | ||||
|  * base, and one message from the queue of the CURL multi events. | ||||
|  | ||||
							
								
								
									
										58
									
								
								libdc/include/dc/session.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								libdc/include/dc/session.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| #ifndef DC_SESSION_H | ||||
| #define DC_SESSION_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #include <dc/api.h> | ||||
| #include <dc/loop.h> | ||||
| #include <dc/account.h> | ||||
| #include <dc/channel.h> | ||||
| #include <dc/gateway.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * A session object will contain all information gathered after a user | ||||
|  * authenticated to the services. It is the "god emperor object" of libdiscord, | ||||
|  * that stores, caches, and provides all information after a log in. From | ||||
|  * available channel objects, account objects of friends, and guild mates, | ||||
|  * to a list of available guilds. | ||||
|  * | ||||
|  * If you consider writing a fully fledged client, or a very sophisticated | ||||
|  * bot, you should consider using this session object in your application. | ||||
|  * | ||||
|  * Please note that the session object attempts to use as little API calls as | ||||
|  * necessary, relying heavily on data provided by the gateway. So it may take | ||||
|  * a bit before appropriate information (i.e. friends) have been loaded. | ||||
|  */ | ||||
| 
 | ||||
| struct dc_session_; | ||||
| typedef struct dc_session_ *dc_session_t; | ||||
| 
 | ||||
| /**
 | ||||
|  * Creates a new session that will attach itself to the given loop. | ||||
|  */ | ||||
| dc_session_t dc_session_new(dc_loop_t loop); | ||||
| 
 | ||||
| /**
 | ||||
|  * Logs the given user out, and clears all internal data. | ||||
|  */ | ||||
| bool dc_session_logout(dc_session_t s); | ||||
| 
 | ||||
| /**
 | ||||
|  * Logs the given user into the system, starts a websocket and begins to | ||||
|  * collect information about the given login user (channels, guilds, | ||||
|  * friends). | ||||
|  */ | ||||
| bool dc_session_login(dc_session_t s, dc_account_t login); | ||||
| 
 | ||||
| bool dc_session_has_token(dc_session_t s); | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns the currently logged in user. Which is often called "@me" in | ||||
|  * Discord API. | ||||
|  */ | ||||
| dc_account_t dc_session_me(dc_session_t s); | ||||
| 
 | ||||
| bool dc_session_equal_me(dc_session_t s, dc_account_t a); | ||||
| bool dc_session_equal_me_fullname(dc_session_t s, char const *a); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										59
									
								
								libdc/src/event.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								libdc/src/event.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| #include <dc/event.h> | ||||
| #include "internal.h" | ||||
| 
 | ||||
| struct dc_event_ | ||||
| { | ||||
|     dc_refable_t ref; | ||||
| 
 | ||||
|     char *type; | ||||
|     json_t *payload; | ||||
| }; | ||||
| 
 | ||||
| static void dc_event_free(dc_event_t e) | ||||
| { | ||||
|     return_if_true(e == NULL,); | ||||
| 
 | ||||
|     free(e->type); | ||||
|     json_decref(e->payload); | ||||
| 
 | ||||
|     free(e); | ||||
| } | ||||
| 
 | ||||
| dc_event_t dc_event_new(char const *type, json_t *payload) | ||||
| { | ||||
|     return_if_true(type == NULL, NULL); | ||||
| 
 | ||||
|     dc_event_t e = calloc(1, sizeof(struct dc_event_)); | ||||
|     return_if_true(e == NULL, NULL); | ||||
| 
 | ||||
|     e->ref.cleanup = (dc_cleanup_t)dc_event_free; | ||||
| 
 | ||||
|     /* A long, long time ago I had a rather quirky software engineering
 | ||||
|      * professor. He taught us C, and everytime string handling would be | ||||
|      * the topic we would wait for one scene: He'd put his index finger on | ||||
|      * his chin, barely touching his lower lip, raise both eyebrows in | ||||
|      * astonishment, and wonder, and then he'd say "strdup" in the worst | ||||
|      * German accent. Even after 15 years that scene stuck with me. | ||||
|      */ | ||||
|     e->type = strdup(type); | ||||
| 
 | ||||
|     if (payload != NULL) { | ||||
|         e->payload = json_incref(payload); | ||||
|     } else { | ||||
|         e->payload = json_null(); | ||||
|     } | ||||
| 
 | ||||
|     return dc_ref(e); | ||||
| } | ||||
| 
 | ||||
| char const *dc_event_type(dc_event_t e) | ||||
| { | ||||
|     return_if_true(e == NULL, NULL); | ||||
|     return e->type; | ||||
| } | ||||
| 
 | ||||
| json_t *dc_event_payload(dc_event_t e) | ||||
| { | ||||
|     return_if_true(e == NULL, NULL); | ||||
|     return e->payload; | ||||
| } | ||||
| @ -14,6 +14,9 @@ struct dc_gateway_ | ||||
| 
 | ||||
|     dc_account_t login; | ||||
| 
 | ||||
|     dc_gateway_event_callback_t callback; | ||||
|     void *callback_data; | ||||
| 
 | ||||
|     uint64_t heartbeat_interval; | ||||
|     time_t last_heartbeat; | ||||
| }; | ||||
| @ -37,6 +40,11 @@ static void dc_gateway_free(dc_gateway_t g) | ||||
|         g->buffer = NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (g->easy != NULL) { | ||||
|         curl_easy_cleanup(g->easy); | ||||
|         g->easy = NULL; | ||||
|     } | ||||
| 
 | ||||
|     dc_unref(g->login); | ||||
| 
 | ||||
|     free(g); | ||||
| @ -72,6 +80,15 @@ void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login) | ||||
|     gw->login = dc_ref(login); | ||||
| } | ||||
| 
 | ||||
| void dc_gateway_set_callback(dc_gateway_t gw, dc_gateway_event_callback_t c, | ||||
|                              void *userdata) | ||||
| { | ||||
|     return_if_true(gw == NULL,); | ||||
| 
 | ||||
|     gw->callback = c; | ||||
|     gw->callback_data = userdata; | ||||
| } | ||||
| 
 | ||||
| bool dc_gateway_connect(dc_gateway_t gw) | ||||
| { | ||||
|     return_if_true(gw == NULL || gw->easy != NULL, true); | ||||
| @ -83,6 +100,15 @@ bool dc_gateway_connect(dc_gateway_t gw) | ||||
|     gw->easy = curl_easy_init(); | ||||
|     goto_if_true(gw->easy == NULL, error); | ||||
| 
 | ||||
|     /* I had already introduced libcurl in a combination with libevent for all
 | ||||
|      * the low level API stuff (i.e. POST/PUT/DELETE), and at the time of writing | ||||
|      * the websocket code it was too late to rip it out, and replace with something | ||||
|      * else (e.g. libwebsockets). | ||||
|      * | ||||
|      * CURL has no inbuilt way to handle websockets (yet), and thus we have to do | ||||
|      * it ourselves by using CONNECT_ONLY. It works, but it is obviously a crutch. | ||||
|      */ | ||||
| 
 | ||||
|     curl_easy_setopt(gw->easy, CURLOPT_URL, DISCORD_GATEWAY); | ||||
|     curl_easy_setopt(gw->easy, CURLOPT_FRESH_CONNECT, 1L); | ||||
|     curl_easy_setopt(gw->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | ||||
| @ -206,7 +232,7 @@ static void dc_gateway_queue_identify(dc_gateway_t gw) | ||||
|     dc_gateway_queue(gw, GATEWAY_OPCODE_IDENTIFY, j); | ||||
| } | ||||
| 
 | ||||
| static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d) | ||||
| static bool dc_gateway_handle_hello(dc_gateway_t gw, char const *s, json_t *d) | ||||
| { | ||||
|     json_t *val = NULL; | ||||
| 
 | ||||
| @ -223,13 +249,23 @@ static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d) | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool dc_gateway_handle_update(dc_gateway_t gw, json_t *d) | ||||
| static bool dc_gateway_handle_update(dc_gateway_t gw, char const *s, json_t *d) | ||||
| { | ||||
|     /* TODO
 | ||||
|      */ | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool dc_gateway_handle_event(dc_gateway_t gw, json_t *d) | ||||
| static bool dc_gateway_handle_event(dc_gateway_t gw, char const *s, json_t *d) | ||||
| { | ||||
|     dc_event_t e = dc_event_new(s, d); | ||||
| 
 | ||||
|     if (gw->callback != NULL && e != NULL) { | ||||
|         gw->callback(gw, e, gw->callback_data); | ||||
|     } | ||||
| 
 | ||||
|     dc_unref(e); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| @ -237,18 +273,24 @@ static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j) | ||||
| { | ||||
|     json_t *val = NULL; | ||||
|     dc_gateway_opcode_t op = 0; | ||||
|     char const *s = NULL; | ||||
| 
 | ||||
|     val = json_object_get(j, "op"); | ||||
|     return_if_true(val == NULL || !json_is_integer(val), false); | ||||
|     op = (dc_gateway_opcode_t)json_integer_value(val); | ||||
| 
 | ||||
|     val = json_object_get(j, "t"); | ||||
|     if (val != NULL && json_is_string(val)) { | ||||
|         s = json_string_value(val); | ||||
|     } | ||||
| 
 | ||||
|     val = json_object_get(j, "d"); | ||||
|     return_if_true(val == NULL || !json_is_object(val), false); | ||||
| 
 | ||||
|     switch (op) { | ||||
|     case GATEWAY_OPCODE_EVENT: dc_gateway_handle_event(gw, val); break; | ||||
|     case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, val); break; | ||||
|     case GATEWAY_OPCODE_UPDATE: dc_gateway_handle_update(gw, val); break; | ||||
|     case GATEWAY_OPCODE_EVENT: dc_gateway_handle_event(gw, s, val); break; | ||||
|     case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, s, val); break; | ||||
|     case GATEWAY_OPCODE_UPDATE: dc_gateway_handle_update(gw, s, val); break; | ||||
|     case GATEWAY_OPCODE_PONG: break; | ||||
|     default: break; | ||||
|     } | ||||
|  | ||||
| @ -18,24 +18,6 @@ struct dc_loop_ | ||||
|     GPtrArray *gateways; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     dc_gateway_t gateway; | ||||
|     struct event *event; | ||||
| } dc_loop_gateway_t; | ||||
| 
 | ||||
| static void dc_loop_gateway_free(dc_loop_gateway_t *p) | ||||
| { | ||||
|     return_if_true(p == NULL,); | ||||
| 
 | ||||
|     if (p->event != NULL) { | ||||
|         event_del(p->event); | ||||
|         event_free(p->event); | ||||
|     } | ||||
| 
 | ||||
|     dc_unref(p->gateway); | ||||
|     free(p); | ||||
| } | ||||
| 
 | ||||
| static void dc_loop_free(dc_loop_t p) | ||||
| { | ||||
|     return_if_true(p == NULL,); | ||||
| @ -175,9 +157,7 @@ dc_loop_t dc_loop_new_full(struct event_base *base, CURLM *multi) | ||||
|     ptr->apis = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); | ||||
|     goto_if_true(ptr->apis == NULL, fail); | ||||
| 
 | ||||
|     ptr->gateways = g_ptr_array_new_with_free_func( | ||||
|         (GDestroyNotify)dc_loop_gateway_free | ||||
|         ); | ||||
|     ptr->gateways = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); | ||||
|     goto_if_true(ptr->gateways == NULL, fail); | ||||
| 
 | ||||
|     ptr->timer = evtimer_new(ptr->base, timer_handler, ptr); | ||||
| @ -217,20 +197,30 @@ void dc_loop_add_api(dc_loop_t l, dc_api_t a) | ||||
|     dc_api_set_event_base(p, l->base); | ||||
|     dc_api_set_curl_multi(p, l->multi); | ||||
| 
 | ||||
|     g_ptr_array_add(l->apis, dc_ref(p)); | ||||
|     g_ptr_array_add(l->apis, p); | ||||
| } | ||||
| 
 | ||||
| void dc_loop_remove_api(dc_loop_t loop, dc_api_t api) | ||||
| { | ||||
|     return_if_true(loop == NULL || api == NULL,); | ||||
| 
 | ||||
|     if (g_ptr_array_find(loop->apis, api, NULL)) { | ||||
|         dc_api_set_event_base(api, NULL); | ||||
|         dc_api_set_curl_multi(api, NULL); | ||||
|         g_ptr_array_remove(loop->apis, api); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void dc_loop_add_gateway(dc_loop_t l, dc_gateway_t gw) | ||||
| { | ||||
|     return_if_true(l == NULL || gw == NULL,); | ||||
|     g_ptr_array_add(l->gateways, dc_ref(gw)); | ||||
| } | ||||
| 
 | ||||
|     dc_loop_gateway_t *ptr = calloc(1, sizeof(dc_loop_gateway_t)); | ||||
|     return_if_true(ptr == NULL,); | ||||
| 
 | ||||
|     ptr->gateway = dc_ref(gw); | ||||
|     ptr->event = NULL; | ||||
| 
 | ||||
|     g_ptr_array_add(l->gateways, ptr); | ||||
| void dc_loop_remove_gateway(dc_loop_t loop, dc_gateway_t gw) | ||||
| { | ||||
|     return_if_true(loop == NULL || gw == NULL,); | ||||
|     g_ptr_array_remove(loop->gateways, gw); | ||||
| } | ||||
| 
 | ||||
| void dc_loop_abort(dc_loop_t l) | ||||
| @ -268,15 +258,15 @@ bool dc_loop_once(dc_loop_t l) | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < l->gateways->len; i++) { | ||||
|         dc_loop_gateway_t *ptr = g_ptr_array_index(l->gateways, i); | ||||
|         dc_gateway_t gw = g_ptr_array_index(l->gateways, i); | ||||
| 
 | ||||
|         if (!dc_gateway_connected(ptr->gateway)) { | ||||
|             if (!dc_gateway_connect(ptr->gateway)) { | ||||
|         if (!dc_gateway_connected(gw)) { | ||||
|             if (!dc_gateway_connect(gw)) { | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         dc_gateway_process(ptr->gateway); | ||||
|         dc_gateway_process(gw); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
|  | ||||
							
								
								
									
										122
									
								
								libdc/src/session.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								libdc/src/session.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| #include <dc/session.h> | ||||
| #include "internal.h" | ||||
| 
 | ||||
| struct dc_session_ | ||||
| { | ||||
|     dc_refable_t ref; | ||||
| 
 | ||||
|     dc_loop_t loop; | ||||
|     dc_api_t api; | ||||
|     dc_account_t login; | ||||
|     dc_gateway_t gateway; | ||||
| }; | ||||
| 
 | ||||
| static void dc_session_free(dc_session_t s) | ||||
| { | ||||
|     return_if_true(s == NULL,); | ||||
| 
 | ||||
|     dc_session_logout(s); | ||||
| 
 | ||||
|     dc_unref(s->api); | ||||
|     dc_unref(s->loop); | ||||
| 
 | ||||
|     free(s); | ||||
| } | ||||
| 
 | ||||
| dc_session_t dc_session_new(dc_loop_t loop) | ||||
| { | ||||
|     return_if_true(loop == NULL, NULL); | ||||
| 
 | ||||
|     dc_session_t s = calloc(1, sizeof(struct dc_session_)); | ||||
|     return_if_true(s == NULL, NULL); | ||||
| 
 | ||||
|     s->ref.cleanup = (dc_cleanup_t)dc_session_free; | ||||
| 
 | ||||
|     s->loop = dc_ref(loop); | ||||
| 
 | ||||
|     s->api = dc_api_new(); | ||||
|     if (s->api == NULL) { | ||||
|         dc_session_free(s); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     dc_loop_add_api(s->loop, s->api); | ||||
| 
 | ||||
|     return dc_ref(s); | ||||
| } | ||||
| 
 | ||||
| bool dc_session_logout(dc_session_t s) | ||||
| { | ||||
|     return_if_true(s == NULL, false); | ||||
| 
 | ||||
|     if (s->login != NULL) { | ||||
|         if (dc_account_has_token(s->login)) { | ||||
|             dc_api_logout(s->api, s->login); | ||||
|         } | ||||
|         dc_unref(s->login); | ||||
|         s->login = NULL; | ||||
|     } | ||||
| 
 | ||||
|     if (s->gateway != NULL) { | ||||
|         dc_loop_remove_gateway(s->loop, s->gateway); | ||||
|         dc_unref(s->gateway); | ||||
|         s->gateway = NULL; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool dc_session_login(dc_session_t s, dc_account_t login) | ||||
| { | ||||
|     return_if_true(s == NULL || login == NULL, false); | ||||
| 
 | ||||
|     if (s->login != NULL) { | ||||
|         dc_session_logout(s); | ||||
|     } | ||||
| 
 | ||||
|     s->login = dc_ref(login); | ||||
|     if (!dc_account_has_token(login)) { | ||||
|         if (!dc_api_authenticate(s->api, s->login)) { | ||||
|             dc_unref(s->login); | ||||
|             s->login = NULL; | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         s->gateway = dc_gateway_new(); | ||||
|         if (s->gateway == NULL) { | ||||
|             dc_session_logout(s); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         dc_gateway_set_login(s->gateway, s->login); | ||||
|         dc_loop_add_gateway(s->loop, s->gateway); | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool dc_session_has_token(dc_session_t s) | ||||
| { | ||||
|     return_if_true(s == NULL || s->login == NULL, false); | ||||
|     return dc_account_has_token(s->login); | ||||
| } | ||||
| 
 | ||||
| dc_account_t dc_session_me(dc_session_t s) | ||||
| { | ||||
|     return_if_true(s == NULL, NULL); | ||||
|     return s->login; | ||||
| } | ||||
| 
 | ||||
| bool dc_session_equal_me(dc_session_t s, dc_account_t a) | ||||
| { | ||||
|     return_if_true(s == NULL || s->login == NULL || a == NULL, false); | ||||
|     return (strcmp(dc_account_fullname(s->login), | ||||
|                    dc_account_fullname(a)) == 0 | ||||
|         ); | ||||
| } | ||||
| 
 | ||||
| bool dc_session_equal_me_fullname(dc_session_t s, char const *a) | ||||
| { | ||||
|     return_if_true(s == NULL || s->login == NULL || a == NULL, false); | ||||
|     return (strcmp(dc_account_fullname(s->login), a) == 0); | ||||
| } | ||||
| @ -31,20 +31,13 @@ | ||||
| #include <dc/api.h> | ||||
| #include <dc/loop.h> | ||||
| #include <dc/account.h> | ||||
| #include <dc/session.h> | ||||
| 
 | ||||
| #define return_if_true(v,r) do { if (v) return r; } while(0) | ||||
| #define goto_if_true(v,l) do { if (v) goto l; } while(0) | ||||
| 
 | ||||
| struct ncdc_account_ { | ||||
|     dc_account_t account; | ||||
|     GPtrArray *friends; | ||||
|     GPtrArray *guilds; | ||||
| }; | ||||
| 
 | ||||
| typedef struct ncdc_account_ *ncdc_account_t; | ||||
| 
 | ||||
| extern GHashTable *accounts; | ||||
| extern dc_account_t current_account; | ||||
| extern GPtrArray *sessions; | ||||
| extern dc_session_t current_session; | ||||
| 
 | ||||
| extern dc_api_t api; | ||||
| extern dc_loop_t loop; | ||||
| @ -55,6 +48,8 @@ extern void *mainwindow; | ||||
| 
 | ||||
| #define KEY_ESCAPE 27 | ||||
| 
 | ||||
| bool is_logged_in(void); | ||||
| 
 | ||||
| wchar_t *util_readkey(int esc, WINDOW *win); | ||||
| 
 | ||||
| void exit_main(void); | ||||
|  | ||||
| @ -4,15 +4,9 @@ | ||||
| static bool | ||||
| ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
| { | ||||
|     bool ret = false; | ||||
|     size_t i = 0; | ||||
|     char c = ' '; | ||||
| 
 | ||||
|     ret = dc_api_get_friends(api, current_account); | ||||
|     if (!ret) { | ||||
|         LOG(n, L"friends: list: failed to fetch your friends"); | ||||
|         return false; | ||||
|     } | ||||
|     dc_account_t current_account = dc_session_me(current_session); | ||||
| 
 | ||||
|     LOG(n, L"/FRIENDS list"); | ||||
|     for (i = 0; i < dc_account_friends_size(current_account); i++) { | ||||
| @ -34,6 +28,7 @@ ncdc_cmd_friends_add(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     char *name = NULL; | ||||
|     dc_account_t friend = NULL; | ||||
|     bool ret = false; | ||||
|     dc_account_t current_account = dc_session_me(current_session); | ||||
| 
 | ||||
|     if (ac <= 1) { | ||||
|         return false; | ||||
| @ -71,6 +66,7 @@ ncdc_cmd_friends_remove(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     dc_account_t friend = NULL; | ||||
|     bool ret = false; | ||||
|     size_t i = 0; | ||||
|     dc_account_t current_account = dc_session_me(current_session); | ||||
| 
 | ||||
|     if (ac <= 1) { | ||||
|         return false; | ||||
| @ -114,6 +110,7 @@ ncdc_cmd_friends_accept(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     dc_account_t friend = NULL; | ||||
|     bool ret = false; | ||||
|     size_t i = 0; | ||||
|     dc_account_t current_account = dc_session_me(current_session); | ||||
| 
 | ||||
|     if (ac <= 1) { | ||||
|         return false; | ||||
| @ -164,8 +161,7 @@ bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     wchar_t *subcmd = NULL; | ||||
|     ncdc_commands_t *it = NULL; | ||||
| 
 | ||||
|     if (current_account == NULL || | ||||
|         !dc_account_has_token(current_account)) { | ||||
|     if (!is_logged_in()) { | ||||
|         LOG(n, L"friends: not logged in"); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| @ -7,51 +7,54 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     char *arg = NULL; | ||||
|     bool ret = false; | ||||
|     dc_account_t acc = NULL; | ||||
|     dc_gateway_t gw = NULL; | ||||
|     dc_session_t s = NULL; | ||||
|     uint32_t idx = 0; | ||||
| 
 | ||||
|     goto_if_true(ac <= 1, cleanup); | ||||
| 
 | ||||
|     arg = w_convert(av[1]); | ||||
|     goto_if_true(arg == NULL, cleanup); | ||||
| 
 | ||||
|     acc = g_hash_table_lookup(accounts, arg); | ||||
|     acc = ncdc_config_account(config, arg); | ||||
|     if (acc == NULL) { | ||||
|         acc = ncdc_config_account(config, arg); | ||||
|         if (acc == NULL) { | ||||
|             LOG(n, L"login: %ls: no such account in configuration", av[1]); | ||||
|         LOG(n, L"login: %ls: no such account in configuration", av[1]); | ||||
|         goto cleanup; | ||||
|     } | ||||
| 
 | ||||
|     ret = g_ptr_array_find_with_equal_func( | ||||
|         sessions, arg, | ||||
|         (GEqualFunc)dc_session_equal_me_fullname, | ||||
|         &idx | ||||
|         ); | ||||
|     if (!ret) { | ||||
|         s = dc_session_new(loop); | ||||
|         if (s == NULL) { | ||||
|             goto cleanup; | ||||
|         } | ||||
| 
 | ||||
|         g_hash_table_insert(accounts, strdup(arg), acc); | ||||
|         g_ptr_array_add(sessions, s); | ||||
|     } else { | ||||
|         if (dc_account_has_token(acc)) { | ||||
|         s = g_ptr_array_index(sessions, idx); | ||||
|         if (dc_session_has_token(s)) { | ||||
|             LOG(n, L"login: %ls: this account is already logged in", av[1]); | ||||
|             goto cleanup; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!dc_api_login(api, acc)) { | ||||
|     if (!dc_session_login(s, acc)) { | ||||
|         LOG(n, L"login: %ls: authentication failed; wrong password?", av[1]); | ||||
|         goto cleanup; | ||||
|     } | ||||
| 
 | ||||
|     gw = dc_gateway_new(); | ||||
|     if (gw == NULL) { | ||||
|         LOG(n, L"login: %ls: failed to establish gateway", av[1]); | ||||
|         goto cleanup; | ||||
|     } | ||||
| 
 | ||||
|     dc_gateway_set_login(gw, acc); | ||||
|     dc_loop_add_gateway(loop, gw); | ||||
| 
 | ||||
|     dc_unref(current_account); | ||||
|     current_account = dc_ref(acc); | ||||
|     dc_unref(current_session); | ||||
|     current_session = dc_ref(s); | ||||
| 
 | ||||
|     LOG(n, L"login: %ls: authentication successful", av[1]); | ||||
|     ret = true; | ||||
| 
 | ||||
| cleanup: | ||||
| 
 | ||||
|     dc_unref(acc); | ||||
|     free(arg); | ||||
| 
 | ||||
|     return ret; | ||||
|  | ||||
| @ -6,19 +6,19 @@ bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
| { | ||||
|     bool ret = false; | ||||
| 
 | ||||
|     goto_if_true(current_account == NULL || | ||||
|                  !dc_account_has_token(current_account), error); | ||||
|     goto_if_true(current_session == NULL || | ||||
|                  !dc_session_has_token(current_session), error); | ||||
| 
 | ||||
|     ret = dc_api_logout(api, current_account); | ||||
|     ret = dc_session_logout(current_session); | ||||
|     if (!ret) { | ||||
|         LOG(n, L"logout: failed to log out the current account"); | ||||
|         goto error; | ||||
|     } | ||||
| 
 | ||||
|     g_hash_table_remove(accounts, dc_account_fullname(current_account)); | ||||
|     g_ptr_array_remove(sessions, current_session); | ||||
| 
 | ||||
|     dc_unref(current_account); | ||||
|     current_account = NULL; | ||||
|     dc_unref(current_session); | ||||
|     current_session = NULL; | ||||
| 
 | ||||
|     LOG(n, L"logout: successfully logged out"); | ||||
| 
 | ||||
|  | ||||
| @ -200,9 +200,10 @@ static void ncdc_mainwindow_render_status(ncdc_mainwindow_t n) | ||||
|     wcsftime(timestr, 99, L"[%H:%M]", t); | ||||
|     fwprintf(f, L"%ls", timestr); | ||||
| 
 | ||||
|     if (current_account == NULL) { | ||||
|     if (!is_logged_in()) { | ||||
|         fwprintf(f, L" [not logged in]"); | ||||
|     } else { | ||||
|         dc_account_t current_account = dc_session_me(current_session); | ||||
|         fwprintf(f, L" [%s]", dc_account_fullname(current_account)); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -12,9 +12,10 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     bool ret = false; | ||||
|     dc_channel_t c = NULL; | ||||
|     ncdc_textview_t v = NULL; | ||||
|     dc_account_t current_account = NULL; | ||||
|     size_t i = 0; | ||||
| 
 | ||||
|     if (current_account == NULL || !dc_account_has_token(current_account)) { | ||||
|     if (is_logged_in()) { | ||||
|         LOG(n, L"msg: not logged in"); | ||||
|         return false; | ||||
|     } | ||||
| @ -22,6 +23,8 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) | ||||
|     target = w_convert(av[1]); | ||||
|     goto_if_true(target == NULL, cleanup); | ||||
| 
 | ||||
|     current_account = dc_session_me(current_session); | ||||
| 
 | ||||
|     /* find out if the target is a friend we can contact
 | ||||
|      */ | ||||
|     dc_account_t f = dc_account_findfriend(current_account, target); | ||||
|  | ||||
| @ -21,10 +21,10 @@ bool thread_done = false; | ||||
| static pthread_t event_thread; | ||||
| static struct event_base *base = NULL; | ||||
| 
 | ||||
| /* all the accounts we have logged into
 | ||||
| /* all the sessions we currently have around
 | ||||
|  */ | ||||
| GHashTable *accounts = NULL; | ||||
| dc_account_t current_account = NULL; | ||||
| GPtrArray *sessions = NULL; | ||||
| dc_session_t current_session = NULL; | ||||
| 
 | ||||
| char *ncdc_private_dir = NULL; | ||||
| void *config = NULL; | ||||
| @ -35,26 +35,17 @@ dc_loop_t loop = NULL; | ||||
|  */ | ||||
| dc_api_t api = NULL; | ||||
| 
 | ||||
| static void cleanup_account(dc_account_t a) | ||||
| { | ||||
|     if (dc_account_has_token(a)) { | ||||
|         dc_api_logout(api, a); | ||||
|     } | ||||
| 
 | ||||
|     dc_unref(a); | ||||
| } | ||||
| 
 | ||||
| static void cleanup(void) | ||||
| { | ||||
|     endwin(); | ||||
| 
 | ||||
|     if (accounts != NULL) { | ||||
|         g_hash_table_unref(accounts); | ||||
|         accounts = NULL; | ||||
|     if (sessions != NULL) { | ||||
|         g_ptr_array_unref(sessions); | ||||
|         sessions = NULL; | ||||
|     } | ||||
| 
 | ||||
|     dc_unref(current_account); | ||||
|     current_account = NULL; | ||||
|     dc_unref(current_session); | ||||
|     current_session = NULL; | ||||
| 
 | ||||
|     thread_done = true; | ||||
|     dc_loop_abort(loop); | ||||
| @ -138,10 +129,8 @@ static bool init_everything(void) | ||||
|     config = ncdc_config_new(); | ||||
|     return_if_true(config == NULL, false); | ||||
| 
 | ||||
|     accounts = g_hash_table_new_full(g_str_hash, g_str_equal, | ||||
|                                      g_free, (GDestroyNotify)cleanup_account | ||||
|         ); | ||||
|     return_if_true(accounts == NULL, false); | ||||
|     sessions = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); | ||||
|     return_if_true(sessions == NULL, false); | ||||
| 
 | ||||
|     ret = pthread_create(&event_thread, NULL, looper, NULL); | ||||
|     return_if_true(ret != 0, false); | ||||
|  | ||||
| @ -1,5 +1,11 @@ | ||||
| #include <ncdc/ncdc.h> | ||||
| 
 | ||||
| bool is_logged_in(void) | ||||
| { | ||||
|     return_if_true(current_session == NULL, false); | ||||
|     return dc_session_has_token(current_session); | ||||
| } | ||||
| 
 | ||||
| wchar_t *util_readkey(int e, WINDOW *win) | ||||
| { | ||||
|     wint_t esc[7] = {0}; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user