diff --git a/CMakeLists.txt b/CMakeLists.txt index ed8a05f46..8275f91df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,6 +276,7 @@ set(LIBFASTFETCH_SRC src/detection/opencl/opencl.c src/detection/os/os.c src/detection/packages/packages.c + src/detection/publicip/publicip.c src/detection/terminalfont/terminalfont.c src/detection/terminalshell/terminalshell.c src/detection/version/version.c diff --git a/src/detection/publicip/publicip.c b/src/detection/publicip/publicip.c new file mode 100644 index 000000000..4c1cdc42f --- /dev/null +++ b/src/detection/publicip/publicip.c @@ -0,0 +1,73 @@ +#include "publicip.h" +#include "common/networking.h" + +static FFNetworkingState state; +static int status = -1; + +void ffPreparePublicIp(FFPublicIpOptions* options) +{ + if (status != -1) + { + fputs("Error: this module can only be used once due to internal limitations\n", stderr); + exit(1); + } + + if (options->url.length == 0) + status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); + else + { + FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); + ffStrbufSubstrAfterFirstS(&host, "://"); + uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); + + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + if(pathStartIndex != host.length) + { + ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex)); + host.length = pathStartIndex; + host.chars[pathStartIndex] = '\0'; + } + + status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); + } +} + +static inline void wrapYyjsonFree(yyjson_doc** doc) +{ + assert(doc); + if (*doc) + yyjson_doc_free(*doc); +} + +const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result) +{ + if (status == -1) + ffPreparePublicIp(options); + + if (status == 0) + return "Failed to connect to an IP detection server"; + + FF_STRBUF_AUTO_DESTROY response = ffStrbufCreateA(4096); + bool success = ffNetworkingRecvHttpResponse(&state, &response, options->timeout); + if (success) ffStrbufSubstrAfterFirstS(&response, "\r\n\r\n"); + + if (!success || response.length == 0) + return "Failed to receive the server response"; + + if (options->url.length == 0) + { + yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(response.chars, response.length, 0, NULL, NULL); + if (doc) + { + yyjson_val* root = yyjson_doc_get_root(doc); + ffStrbufAppendS(&result->ip, yyjson_get_str(yyjson_obj_get(root, "ip"))); + ffStrbufDestroy(&result->location); + ffStrbufInitF(&result->location, "%s, %s", yyjson_get_str(yyjson_obj_get(root, "city")), yyjson_get_str(yyjson_obj_get(root, "country"))); + return NULL; + } + } + + ffStrbufDestroy(&result->ip); + ffStrbufInitMove(&result->ip, &response); + return NULL; +} diff --git a/src/detection/publicip/publicip.h b/src/detection/publicip/publicip.h new file mode 100644 index 000000000..31ab394f4 --- /dev/null +++ b/src/detection/publicip/publicip.h @@ -0,0 +1,10 @@ +#include "fastfetch.h" + +typedef struct FFPublicIpResult +{ + FFstrbuf ip; + FFstrbuf location; +} FFPublicIpResult; + +void ffPreparePublicIp(FFPublicIpOptions* options); +const char* ffDetectPublicIp(FFPublicIpOptions* options, FFPublicIpResult* result); diff --git a/src/modules/publicip/publicip.c b/src/modules/publicip/publicip.c index fdaf7b9f6..21ed3850b 100644 --- a/src/modules/publicip/publicip.c +++ b/src/modules/publicip/publicip.c @@ -1,98 +1,43 @@ #include "common/printing.h" #include "common/jsonconfig.h" -#include "common/networking.h" #include "modules/publicip/publicip.h" +#include "detection/publicip/publicip.h" #include "util/stringUtils.h" #define FF_PUBLICIP_DISPLAY_NAME "Public IP" #define FF_PUBLICIP_NUM_FORMAT_ARGS 1 -static FFNetworkingState state; -static int status = -1; - -static inline void wrapYyjsonFree(yyjson_doc** doc) -{ - assert(doc); - if (*doc) - yyjson_doc_free(*doc); -} - -void ffPreparePublicIp(FFPublicIpOptions* options) -{ - if (status != -1) - { - fputs("Error: " FF_PUBLICIP_DISPLAY_NAME " can only be used once due to internal limitations\n", stderr); - exit(1); - } - - if (options->url.length == 0) - status = ffNetworkingSendHttpRequest(&state, "ipinfo.io", "/json", NULL); - else - { - FF_STRBUF_AUTO_DESTROY host = ffStrbufCreateCopy(&options->url); - ffStrbufSubstrAfterFirstS(&host, "://"); - uint32_t pathStartIndex = ffStrbufFirstIndexC(&host, '/'); - - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); - if(pathStartIndex != host.length) - { - ffStrbufAppendNS(&path, pathStartIndex, host.chars + (host.length - pathStartIndex)); - host.length = pathStartIndex; - host.chars[pathStartIndex] = '\0'; - } - - status = ffNetworkingSendHttpRequest(&state, host.chars, path.length == 0 ? "/" : path.chars, NULL); - } -} - void ffPrintPublicIp(FFPublicIpOptions* options) { - if (status == -1) - ffPreparePublicIp(options); + FFPublicIpResult result; + ffStrbufInit(&result.ip); + ffStrbufInit(&result.location); + const char* error = ffDetectPublicIp(options, &result); - if (status == 0) + if (error) { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to connect to an IP detection server"); - return; - } - - FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(4096); - bool success = ffNetworkingRecvHttpResponse(&state, &result, options->timeout); - if (success) ffStrbufSubstrAfterFirstS(&result, "\r\n\r\n"); - - if (!success || result.length == 0) - { - ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to receive the server response"); + ffPrintError(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, error); return; } if (options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - - if (options->url.length == 0) - { - yyjson_doc* __attribute__((__cleanup__(wrapYyjsonFree))) doc = yyjson_read_opts(result.chars, result.length, 0, NULL, NULL); - if (doc) - { - yyjson_val* root = yyjson_doc_get_root(doc); - printf("%s (%s, %s)\n", - yyjson_get_str(yyjson_obj_get(root, "ip")), - yyjson_get_str(yyjson_obj_get(root, "city")), - yyjson_get_str(yyjson_obj_get(root, "country")) - ); - return; - } - } - - ffStrbufPutTo(&result, stdout); + if (result.location.length) + printf("%s (%s)", result.ip.chars, result.location.chars); + else + ffStrbufPutTo(&result.ip, stdout); } else { ffPrintFormat(FF_PUBLICIP_DISPLAY_NAME, 0, &options->moduleArgs, FF_PUBLICIP_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &result} + {FF_FORMAT_ARG_TYPE_STRBUF, &result.ip}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.location}, }); } + + ffStrbufDestroy(&result.ip); + ffStrbufDestroy(&result.location); } void ffInitPublicIpOptions(FFPublicIpOptions* options)