diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bda4bc0 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CC=gcc + +all: gemcurl + +gemlib.o: src/gemlib.c src/gemlib.h + mkdir -p build + $(CC) -o build/gemlib.o -c src/gemlib.c + +gemcurl.o: src/gemlib.h src/gemcurl.c + mkdir -p build + $(CC) -o build/gemcurl.o -c src/gemcurl.c + +gemcurl: gemcurl.o gemlib.o + mkdir -p build + $(CC) -o build/gemcurl build/gemcurl.o build/gemlib.o -lcrypto -lssl diff --git a/gemini.c b/gemini.c deleted file mode 100644 index 260966a..0000000 --- a/gemini.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include -#include - -typedef struct _gemini_request { - char request_content[1024]; - char hostname[256]; - char scheme[64]; - char path[1024]; - char answer_code[2]; - char answer_meta[2]; - char answer_content[8192]; -} Request; - -Request *examineRequest(const char *request_content) -{ - int i = 0, len = 0, len_req = strlen(request_content); - Request *request = malloc(sizeof(Request)); - - if (request == NULL) - return NULL; - - memset(request, 0, sizeof(Request)); - - strcpy(request->request_content, request_content); - - /* We are trying to determine the protocol */ - while (i <= len_req - 3) - if (request_content[i] != ':' || request_content[i + 1] != '/' || request_content[i + 2] != '/') - { - i++; - len++; - } - else - { - strncpy(request->scheme, request_content + i - len, len); - break; - } - - /* If no protocol found error */ - if (request->scheme[0] == 0) - { - free(request); - return NULL; - } - - /* We skip the :// */ - i += 3; - len = 0; - - /* We try to determine hostname + port */ - while (i < len_req && request_content[i] != '/') - { - i++; - len++; - } - - strncpy(request->hostname, request_content + i - len, len); - - /* if no hostname error */ - if (request->hostname[0] == 0) - { - free(request); - return NULL; - } - - /* now fill path in request struct */ - strncpy(request->path, request_content + i, strlen(request_content) - i); - - /* if path is empty, so path is root */ - if(request->path[0] == 0) - strcpy(request->path, "/"); - - return request; -} - -int main(void) -{ - Request *request = examineRequest("http://[5f1f:fgeg::1]"); - - if (request == NULL) - { - printf("error during url parsing\n"); - return 1; - } - - printf("Request : %s\nScheme : %s\nHostname : %s\n", request->request_content, request->scheme, request->hostname); - - free(request); - - return 0; -} diff --git a/src/gemcurl.c b/src/gemcurl.c new file mode 100644 index 0000000..4382bea --- /dev/null +++ b/src/gemcurl.c @@ -0,0 +1,103 @@ +#include +#include +#include + +#include "gemlib.h" + +#define VERSION_MAJ 0 +#define VERSION_MIN 0 +#define VERSION_REV 0 +#define NAME "gemcurl" + +void printName (char *extra); +void printVersion (void); +void printLicence (void); +void printUsage (char *command); +void printHelp(void); + +int main (int argc, char **argv) +{ + if (argc < 2) + { + printUsage(argv[0]); + + return EXIT_FAILURE; + } + + if (strcmp(argv[1], "-v") == 0 | strcmp(argv[1], "--version") == 0) + { + printName(NULL); + printVersion(); + printLicence(); + + return EXIT_SUCCESS; + } + + if (strcmp(argv[1], "-h") == 0 | strcmp(argv[1], "--help") == 0) + { + printName("A dead simple Gemini client"); + printVersion(); + printf("\n"); + printUsage(argv[0]); + printHelp(); + printf("\n"); + printLicence(); + + return EXIT_SUCCESS; + } + + if (strcmp(argv[1], "-g") == 0 | strcmp(argv[1], "--get") == 0) + { + if (argc < 3) + { + printUsage(argv[0]); + + return EXIT_FAILURE; + } + + if (getGem(argv[2], stdout) != 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; + } + + printUsage(argv[0]); + + return EXIT_FAILURE; +} + +void printName (char *extra) +{ + if (extra == NULL) + printf("%s\n", NAME); + else + printf("%s : %s\n", NAME, extra); +} + +void printVersion (void) +{ + if (VERSION_MIN == 0 && VERSION_REV == 0) + printf("Version : %d\n", VERSION_MAJ); + else if (VERSION_REV == 0) + printf("Version : %d.%d\n", VERSION_MAJ, VERSION_MIN); + else + printf("Version : %d.%d.%d\n", VERSION_MAJ, VERSION_MIN, VERSION_REV); +} + +void printLicence (void) +{ + printf("Written by Romain de Laage, Licence GPLv3\n"); +} + +void printUsage (char *command) +{ + if (command == NULL) + printf("Usage : gemcurl CMD [URL]\n"); + else + printf("Usage : %s CMD [URL]\n", command); +} + +void printHelp (void) +{ + printf("CMD :\n\t-h | --help : Show this help\n\t-v | --version : Show version\n\t-g | --get : Get a gemini content, must be followed by the URL\n"); +} diff --git a/src/gemlib.c b/src/gemlib.c new file mode 100644 index 0000000..c8ee7b7 --- /dev/null +++ b/src/gemlib.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gemlib.h" + +Request *examineRequest(const char *request_content) +{ + int i = 0, len = 0, len_req = strlen(request_content); + Request *request = malloc(sizeof(Request)); + + if (request == NULL) + return NULL; + + memset(request, 0, sizeof(Request)); + + strcpy(request->request_content, request_content); + strcat(request->request_content, "\r\n"); + + /* We are trying to determine the protocol */ + while (i <= len_req - 3) + if (request_content[i] != ':' || request_content[i + 1] != '/' || request_content[i + 2] != '/') + { + i++; + len++; + } + else + { + strncpy(request->scheme, request_content + i - len, len); + break; + } + + /* If no protocol found error */ + if (request->scheme[0] == 0) + { + free(request); + return NULL; + } + + /* We skip the :// */ + i += 3; + len = 0; + + /* We try to determine hostname + port */ + while (i < len_req && request_content[i] != '/') + { + i++; + len++; + } + + strncpy(request->hostname, request_content + i - len, len); + + /* if no hostname error */ + if (request->hostname[0] == 0) + { + free(request); + return NULL; + } + + /* now fill path in request struct */ + strncpy(request->path, request_content + i, strlen(request_content) - i); + + /* if path is empty, so path is root */ + if (request->path[0] == 0) + strcpy(request->path, "/"); + + return request; +} + +void requestTheServer(Request *request, FILE *answerFile) +{ + const SSL_METHOD *method = TLS_client_method(); + SSL *ssl = NULL; + SSL_CTX *ctx = NULL; + BIO *bio = NULL; + int n; + + if (method == NULL) + { + perror("Method"); + ERR_print_errors_fp(stderr); + exit(1); + } + + ctx = SSL_CTX_new(method); + + if (ctx == NULL) + { + perror("ctx"); + ERR_print_errors_fp(stderr); + exit(2); + } + + bio = BIO_new_ssl_connect(ctx); + + if (bio == NULL) + { + perror("bio"); + ERR_print_errors_fp(stderr); + SSL_CTX_free(ctx); + exit(3); + } + + BIO_get_ssl(bio, &ssl); + SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); + BIO_set_conn_hostname(bio, request->hostname); + + if (BIO_do_connect(bio) <= 0) + { + BIO_free_all(bio); + SSL_CTX_free(ctx); + perror("bio connect"); + ERR_print_errors_fp(stderr); + exit(4); + } + + BIO_puts(bio, request->request_content); + + while (1) + { + memset(request->answer_content, 0, sizeof(request->answer_content)); + n = BIO_read(bio, request->answer_content, 1024); + if (n <= 0) break; + fprintf(answerFile, "%s", request->answer_content); + } + + BIO_free_all(bio); + SSL_CTX_free(ctx); +} + +void initSSL (void) +{ + SSL_load_error_strings(); + SSL_library_init(); +} + +int getGem (const char *URL, FILE *answerFile) +{ + Request *request = examineRequest(URL); + + initSSL(); + + if (request == NULL) + { + printf("error during url parsing\n"); + return 1; + } + + requestTheServer(request, answerFile); + + free(request); + + return 0; +} diff --git a/src/gemlib.h b/src/gemlib.h new file mode 100644 index 0000000..e471512 --- /dev/null +++ b/src/gemlib.h @@ -0,0 +1,22 @@ +#ifndef _GEMLIB_H +#define _GEMLIB_H + +typedef struct _gemini_request { + char request_content[1024]; + char hostname[256]; + char scheme[64]; + char path[1024]; + char answer_code[2]; + char answer_meta[2]; + char answer_content[8192]; +} Request; + +Request *examineRequest(const char *request_content); + +void requestTheServer(Request *request, FILE *answerFile); + +void initSSL (void); + +int getGem (const char *URL, FILE *answerFile); + +#endif diff --git a/src/main.vala b/src/main.vala new file mode 100644 index 0000000..c14ab1a --- /dev/null +++ b/src/main.vala @@ -0,0 +1,39 @@ +extern char *test(); +int main (string[] args) +{ + Gtk.init(ref args); + + var window = new Gtk.Window (); + window.border_width = 10; + window.window_position = Gtk.WindowPosition.CENTER; + window.set_default_size (350, 80); + + var header_bar = new Gtk.HeaderBar (); + header_bar.set_title("This is a test"); + header_bar.set_show_close_button(true); + + var url_bar = new Gtk.Entry (); + header_bar.pack_start(url_bar); + + var url_go_button = new Gtk.Button.with_label("Go !"); + header_bar.pack_start(url_go_button); + + window.destroy.connect (Gtk.main_quit); + + window.set_titlebar(header_bar); + + var content = new Gtk.TextView(); + content.set_editable(false); + + window.add(content); + + window.show_all (); + +// content.buffer.text = (string)test(); + content.buffer.text = "This is an example of text."; + content.set_cursor_visible(false); + + Gtk.main(); + + return 0; +}