summaryrefslogtreecommitdiff
path: root/sakisafecli
diff options
context:
space:
mode:
Diffstat (limited to 'sakisafecli')
-rw-r--r--sakisafecli/.clang-format166
-rw-r--r--sakisafecli/Makefile19
-rw-r--r--sakisafecli/config.c69
-rw-r--r--sakisafecli/config.h9
-rw-r--r--sakisafecli/funcs.c56
-rw-r--r--sakisafecli/funcs.h24
-rw-r--r--sakisafecli/options.h31
-rw-r--r--sakisafecli/sakisafecli.161
-rw-r--r--sakisafecli/sakisafecli.c285
-rw-r--r--sakisafecli/sakisafecli.h37
-rw-r--r--sakisafecli/sakisafeclirc.573
11 files changed, 830 insertions, 0 deletions
diff --git a/sakisafecli/.clang-format b/sakisafecli/.clang-format
new file mode 100644
index 0000000..c834b51
--- /dev/null
+++ b/sakisafecli/.clang-format
@@ -0,0 +1,166 @@
+---
+Language: Cpp
+# BasedOnStyle: Mozilla
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveMacros: None
+AlignConsecutiveAssignments: None
+AlignConsecutiveBitFields: None
+AlignConsecutiveDeclarations: None
+AlignEscapedNewlines: Right
+AlignOperands: Align
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortEnumsOnASingleLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: TopLevel
+AlwaysBreakAfterReturnType: TopLevel
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+AttributeMacros:
+ - __capability
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: Never
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: true
+ AfterExternBlock: true
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: true
+BreakBeforeBraces: Mozilla
+BreakBeforeInheritanceComma: true
+BreakInheritanceList: BeforeComma
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 5
+ContinuationIndentWidth: 5
+Cpp11BracedListStyle: false
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+StatementAttributeLikeMacros:
+ - Q_EMIT
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '(Test)?$'
+IncludeIsMainSourceRegex: ''
+IndentCaseLabels: true
+IndentCaseBlocks: false
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentExternBlock: AfterExternBlock
+IndentRequires: false
+IndentWidth: 5
+IndentWrappedFunctionNames: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 5
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakAssignment: 5
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PenaltyIndentedWhitespace: 0
+PointerAlignment: Right
+ReflowComments: true
+SortIncludes: false
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: false
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyBlock: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+SpaceBeforeSquareBrackets: false
+BitFieldColonSpacing: Both
+Standard: Latest
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 5
+UseCRLF: false
+UseTab: Always
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
+...
+
diff --git a/sakisafecli/Makefile b/sakisafecli/Makefile
new file mode 100644
index 0000000..d0c2c36
--- /dev/null
+++ b/sakisafecli/Makefile
@@ -0,0 +1,19 @@
+
+PROG += sakisafecli
+SRCS += funcs.c sakisafecli.c config.c
+MAN += sakisafecli.1 sakisafeclirc.5
+LDADD += -lssl -lz -lpthread -lnghttp2 -lcurl -lconfig -lcrypto -L/usr/local/lib
+PREFIX = /usr/local
+
+# Use libbsd features if wanted
+use-libbsd ?= no
+.if ${use-libbsd} == "yes"
+CFLAGS += -Duse_libbsd
+LDADD += -lbsd
+.endif
+
+BINMODE = 755
+BINDIR = $(PREFIX)/bin
+MANDIR = $(PREFIX)/man/man
+
+.include <bsd.prog.mk>
diff --git a/sakisafecli/config.c b/sakisafecli/config.c
new file mode 100644
index 0000000..8fce884
--- /dev/null
+++ b/sakisafecli/config.c
@@ -0,0 +1,69 @@
+#include <libconfig.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "options.h"
+#include "config.h"
+
+void
+print_config()
+{
+ puts("Current configuration:");
+ printf("Server: %s\n", server);
+ if(socks_proxy_flag)
+ printf("Socks proxy url: %s", socks_proxy_url);
+ if(http_proxy_flag)
+ printf("HTTP proxy url: %s", http_proxy_url);
+ if(silent_flag)
+ puts("Silent: true");
+ else
+ puts("Silent: false");
+ if(ipv6_flag)
+ printf("Force IPv6: true\n");
+ else
+ printf("Force IPv6: false\n");
+ if(ipv4_flag)
+ printf("Force IPv4: true\n");
+ else
+ printf("Force IPv4: false\n");
+
+ return;
+}
+config_t runtime_config;
+void
+parse_config_file(FILE *config)
+{
+
+ config_init(&runtime_config);
+ config_read(&runtime_config, config);
+ config_setting_t *cur;
+ cur = config_lookup(&runtime_config, "server");
+
+ if(config != NULL) {
+ if(cur != NULL)
+ server = (char *)config_setting_get_string(cur);
+ cur = config_lookup(&runtime_config, "socks_proxy");
+ if(cur != NULL)
+ socks_proxy_url = (char *)config_setting_get_string(cur);
+ cur = config_lookup(&runtime_config, "http_proxy");
+ if(cur != NULL)
+ http_proxy_url = (char *)config_setting_get_string(cur);
+ cur = config_lookup(&runtime_config, "use_socks_proxy");
+ if(cur != NULL)
+ socks_proxy_flag = config_setting_get_bool(cur);
+ cur = config_lookup(&runtime_config, "use_http_proxy");
+ if(cur != NULL)
+ http_proxy_flag = config_setting_get_bool(cur);
+ cur = config_lookup(&runtime_config, "silent");
+ if(cur != NULL)
+ silent_flag = config_setting_get_bool(cur);
+ cur = config_lookup(&runtime_config, "force_ipv6");
+ if(cur != NULL)
+ ipv6_flag = config_setting_get_bool(cur);
+ cur = config_lookup(&runtime_config, "force_ipv4");
+ if(cur != NULL)
+ ipv4_flag = config_setting_get_bool(cur);
+ cur = config_lookup(&runtime_config, "key");
+ if(cur != NULL)
+ ssh_key_path = (char *)config_setting_get_string(cur);
+ }
+}
diff --git a/sakisafecli/config.h b/sakisafecli/config.h
new file mode 100644
index 0000000..ede4bc2
--- /dev/null
+++ b/sakisafecli/config.h
@@ -0,0 +1,9 @@
+#include <libconfig.h>
+
+/* Parse the config file */
+void
+parse_config_file(FILE *config);
+
+/* Print the current settings */
+void
+print_config();
diff --git a/sakisafecli/funcs.c b/sakisafecli/funcs.c
new file mode 100644
index 0000000..d7782ff
--- /dev/null
+++ b/sakisafecli/funcs.c
@@ -0,0 +1,56 @@
+#include <curl/system.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libconfig.h>
+#include <curl/curl.h>
+#include <stdbool.h>
+#include "sakisafecli.h"
+
+size_t
+write_data(void *buffer, size_t size, size_t nmemb, void *userp)
+{
+ memcpy(userp, buffer, nmemb * size);
+ return 0;
+}
+
+void
+print_usage()
+{
+ printf("USAGE: sakisafecli [-p socks proxy|-P http proxy] [-x] [-s] "
+ "[-6|-4] [--server] FILE\n");
+ return;
+}
+
+void
+print_help()
+{
+ printf("-s|--server: specifies the sakisafe "
+ "server\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s",
+ "-t|--token: Authentication token (https://u.kalli.st)",
+ "-P|--http-proxy: http proxy to use e.g. http://127.0.0.1:4444",
+ "-p|--socks-proxy: SOCK proxy to use e.g. 127.0.0.1:9050",
+ "-6|--ipv6: uses IPv6 only",
+ "-4|--ipv6: uses IPv4 only",
+ "-S|--silent: doesn't print progress",
+ "-x|--paste: read file from stdin",
+ "-C: print current settings",
+ "-h|--help: print this message.\n");
+ return;
+}
+
+size_t
+progress(
+ void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
+{
+ /* I don't know why the fuck I have to do this */
+ if(ultotal == 0) {
+ ultotal++;
+ }
+ printf("\r%li uploaded of %li (\033[32;1m%li%%\033[30;0m)",
+ ulnow,
+ ultotal,
+ ulnow * 100 / ultotal);
+ fflush(stdout);
+ return 0;
+}
diff --git a/sakisafecli/funcs.h b/sakisafecli/funcs.h
new file mode 100644
index 0000000..def8f15
--- /dev/null
+++ b/sakisafecli/funcs.h
@@ -0,0 +1,24 @@
+#pragma once
+#include <stdlib.h>
+
+/* Function used from curl to write data to a buffer */
+size_t
+write_data(void *buffer, size_t size, size_t nmemb, void *userp);
+
+/* Print usage message (when nothing is given to sakisafecli) */
+void
+print_usage();
+
+/* Print help message (-h) */
+void
+print_help();
+
+/* Function used to display progress when uploading a file */
+
+void
+progress(
+ void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
+
+/* Print config */
+void
+print_config();
diff --git a/sakisafecli/options.h b/sakisafecli/options.h
new file mode 100644
index 0000000..d92a575
--- /dev/null
+++ b/sakisafecli/options.h
@@ -0,0 +1,31 @@
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <libconfig.h>
+/* clainsafecli options */
+
+/* Default server you'll upload files to */
+extern char *server;
+
+/* proxy urls, socks and http. in that order, by default they're
+ * configured to be used for tor and i2p, but if you have another
+ * socks/http proxy, you can set it here.
+ */
+
+/* Enable "history" files and where to store that file */
+
+extern char history_file_path[256];
+extern const int enable_links_history;
+extern const char *path;
+
+/* Config file variables */
+extern char *socks_proxy_url, *http_proxy_url;
+
+extern bool socks_proxy_flag;
+extern bool http_proxy_flag;
+extern bool ipv6_flag;
+extern bool ipv4_flag;
+extern bool silent_flag;
+extern char *ssh_key_path;
+extern config_t runtime_config;
+#endif /* OPTIONS_H */
diff --git a/sakisafecli/sakisafecli.1 b/sakisafecli/sakisafecli.1
new file mode 100644
index 0000000..1a73db7
--- /dev/null
+++ b/sakisafecli/sakisafecli.1
@@ -0,0 +1,61 @@
+.Dd $Mdocdate$
+.Dt sakisafecli 1
+.Os
+.Sh sakisafecli
+.Nm sakisafecli
+.Nd file uploader
+
+.Sh SYPNOSIS
+
+sakisafecli [https]://sakisafe.tld FILE
+
+.Sh DESCRIPTION
+sakisafecli is a file uploader. Intended for sakisafe, but also works
+for 0x0.st, 0xff.i2p, i.kalli.st and probably others. It also supports
+transfer via scp.
+.Sh OPTIONS
+
+.Sy --http_proxy
+Use an HTTP Proxy to make the request to the server.
+
+.Sy --socks_proxy
+Use a SOCKS Proxy to make the request to the server.
+
+.Sy --server
+Specify which server to use to upload the given file.
+
+.Sy -4
+Force an IPv4 connection.
+
+.Sy -6
+Force an IPv6 connection.
+
+.Sy -x
+Paste mode. Reads from stdin and publish the given content to the
+server.
+
+.Sy -C
+Print the current settings.
+
+.Sy --key
+path to the private SSH key to use. Only useful when using the
+.Sy scp://
+protocol.
+
+.Sh BUGS
+Of course.
+.Sh HISTORY
+
+First commit to
+.Sy lainsafe
+was made in 2020-04-19.
+.Sy clainsafecli
+first commit was made in 2021-02-01.
+.Sy clainsafecli
+was rebranded to
+.Sy sakisafecli
+in 2022-05-05
+.Sh AUTHORS
+Raoul Vaughn <chief_keef at riseup dot net>
+.Sh SEE ALSO
+sakisafeclirc(5)
diff --git a/sakisafecli/sakisafecli.c b/sakisafecli/sakisafecli.c
new file mode 100644
index 0000000..3c4e94f
--- /dev/null
+++ b/sakisafecli/sakisafecli.c
@@ -0,0 +1,285 @@
+#include <libconfig.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <err.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <curl/curl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#ifdef use_libbsd
+#include <bsd/string.h>
+#endif
+#include "curl/easy.h"
+#include "options.h"
+#include "config.h"
+#include "funcs.h"
+#include "sakisafecli.h"
+
+/* Config variables */
+
+bool ipv6_flag = false, ipv4_flag = false, http_proxy_flag = false,
+ socks_proxy_flag = false, silent_flag = false, paste_flag = false;
+
+char *http_proxy_url, *socks_proxy_url;
+char *ssh_key_path = NULL;
+
+char *server = "https://lainsafe.delegao.moe";
+const char *path = ".cache/sakisafelinks";
+
+int
+main(int argc, char **argv)
+{
+#ifdef __OpenBSD__
+ if(pledge("stdio rpath cpath inet dns unveil tmppath", "") == -1) {
+ err(1, "pledge");
+ _exit(-1);
+ }
+#endif
+
+ char *form_key = "file";
+
+ char *buffer = (char *)calloc(1024, sizeof(char));
+
+ if(buffer == NULL) {
+ fprintf(stderr, "Error allocating memory!\n");
+ return -1;
+ }
+ char config_location[512];
+ char *sakisafeclirc_env = getenv("SAKISAFECLIRC");
+
+ if(sakisafeclirc_env == NULL) {
+ snprintf(config_location, 512, "%s/.sakisafeclirc", getenv("HOME"));
+ FILE *fp = fopen(config_location, "r");
+ if(fp != NULL) {
+ parse_config_file(fp);
+ fclose(fp);
+ }
+ } else {
+#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(use_libbsd)
+ strlcpy(config_location, sakisafeclirc_env, 512);
+#else /* Linux sucks! */
+ strncpy(config_location, sakisafeclirc_env, 512);
+#endif
+ FILE *fp = fopen(config_location, "r");
+ if(fp != NULL) {
+ parse_config_file(fp);
+ fclose(fp);
+ }
+ }
+ /* libcurl initialization */
+
+ CURL *easy_handle = curl_easy_init();
+
+ if(!easy_handle) {
+ fprintf(stderr, "Error initializing libcurl\n");
+ return -1;
+ }
+
+ if(argc == optind) {
+ print_usage();
+ free(buffer);
+ curl_easy_cleanup(easy_handle);
+ return -1;
+ }
+
+ int option_index = 0;
+ static struct option long_options[] = {
+ { "server", required_argument, 0, 's' },
+ { "help", no_argument, 0, 'h' },
+ { "socks-proxy", required_argument, 0, 'p' },
+ { "http-proxy", required_argument, 0, 'P' },
+ { "silent", no_argument, 0, 'S' },
+ { "ipv4", no_argument, 0, '4' },
+ { "ipv6", no_argument, 0, '6' },
+ { "paste", no_argument, 0, 'x' },
+ { "key", required_argument, 0, 'k' },
+ { 0, 0, 0, 0 }
+ };
+
+ int c = 0;
+ while(
+ (c = getopt_long(
+ argc, argv, "46hT:p:P:Ss:xCk:", long_options, &option_index)) !=
+ -1) {
+ switch(c) {
+ case 's':
+ server = optarg;
+ break;
+ case 'h':
+ print_help();
+ free(buffer);
+ curl_easy_cleanup(easy_handle);
+ return 0;
+ break;
+ case 'p':
+ socks_proxy_url = optarg;
+ socks_proxy_flag = true;
+ break;
+ case 'P':
+ http_proxy_url = optarg;
+ http_proxy_flag = true;
+ break;
+ case 'S':
+ silent_flag = true;
+ break;
+ case '4':
+ ipv4_flag = true;
+ break;
+ case '6':
+ ipv6_flag = true;
+ break;
+ case 'x':
+ /* We don't want the progress bar in this case */
+ silent_flag = true;
+ paste_flag = true;
+ break;
+ case '?':
+ print_usage();
+ return 0;
+ break;
+ case 'C':
+ print_config();
+ return 0;
+ case 'k':
+ ssh_key_path = optarg;
+ break;
+ default:
+ print_usage();
+ return 0;
+ break;
+ }
+ }
+
+ if(access(argv[optind], F_OK) && !paste_flag) {
+ fprintf(stderr, "Error opening file: %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* curl options */
+ curl_easy_setopt(easy_handle, CURLOPT_USERAGENT, "curl");
+ curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, write_data);
+ curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, buffer);
+
+ curl_easy_setopt(easy_handle, CURLOPT_URL, server);
+
+ int protocol = get_protocol(server);
+
+ /* Proxy options */
+
+ if(socks_proxy_flag && http_proxy_flag) {
+ fprintf(stderr, "Socks_Proxy and HTTP_PROXY can't be used at once\n");
+ return -1;
+ } else if(socks_proxy_flag) {
+ curl_easy_setopt(easy_handle, CURLOPT_PROXY, socks_proxy_url);
+ curl_easy_setopt(
+ easy_handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
+ } else if(http_proxy_flag && protocol == CURLPROTO_HTTP) {
+ curl_easy_setopt(easy_handle, CURLOPT_PROXY, http_proxy_url);
+ curl_easy_setopt(easy_handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ }
+
+ /* Which address to use */
+
+ if(ipv6_flag)
+ curl_easy_setopt(easy_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
+ else if(ipv4_flag)
+ curl_easy_setopt(easy_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+ else
+ curl_easy_setopt(
+ easy_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER);
+
+ /* Common options for both HTTP and SCP transfers */
+
+ curl_easy_setopt(easy_handle, CURLOPT_NOPROGRESS, silent_flag);
+ struct progress mem;
+ curl_easy_setopt(easy_handle, CURLOPT_XFERINFODATA, &mem);
+ curl_easy_setopt(easy_handle, CURLOPT_XFERINFOFUNCTION, progress);
+
+ /* File name */
+
+ /* TODO: make it iterate on args so you can upload multiple files
+ * at once (sakisafecli file1 file2 ... filen)
+ */
+
+ /* Process HTTP uploads */
+
+ if(protocol == CURLPROTO_HTTP) {
+ curl_mime *mime;
+ mime = curl_mime_init(easy_handle);
+
+ curl_easy_setopt(easy_handle, CURLOPT_MIMEPOST, mime);
+ if(!mime) {
+ fprintf(stderr, "Error initializing curl_mime\n");
+ }
+
+ curl_mimepart *file_data;
+ file_data = curl_mime_addpart(mime);
+ char *filename = argv[optind];
+
+ if(paste_flag)
+ filename = "/dev/stdin";
+
+ curl_mime_filedata(file_data, filename);
+ curl_mime_name(file_data, form_key);
+ if(paste_flag)
+ curl_mime_filename(file_data, "-");
+
+ curl_easy_perform(easy_handle);
+ if(!silent_flag)
+ putchar('\n');
+ puts(buffer);
+ curl_mime_free(mime);
+
+ }
+ /* Process SCP uploads */
+ else if(protocol == CURLPROTO_SCP) {
+ curl_easy_setopt(
+ easy_handle, CURLOPT_SSH_PRIVATE_KEYFILE, ssh_key_path);
+
+ char path[256];
+ char *filename = argv[optind];
+
+ curl_easy_setopt(easy_handle, CURLOPT_UPLOAD, true);
+ FILE *fp = fopen(filename, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "%s", strerror(errno));
+ exit(-1);
+ }
+
+ struct stat st;
+ stat(argv[optind], &st);
+ snprintf(path, 256, "%s/%s", server, filename);
+ curl_easy_setopt(easy_handle, CURLOPT_READDATA, fp);
+ curl_easy_setopt(
+ easy_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)st.st_size);
+
+ int ret = curl_easy_perform(easy_handle);
+ putchar('\n');
+ if(ret != 0) {
+ fprintf(stderr, "%i: %s\n", ret, curl_easy_strerror(ret));
+ }
+
+ } else {
+ puts("Unsupported protocol");
+ return -1;
+ }
+
+ curl_easy_cleanup(easy_handle);
+ config_destroy(&runtime_config);
+ free(buffer);
+ return 0;
+}
+
+int
+get_protocol(char *server)
+{
+ if(strstr(server, "http://") != NULL || strstr(server, "https://"))
+ return CURLPROTO_HTTP;
+ else if(strstr(server, "scp://") != NULL)
+ return CURLPROTO_SCP;
+ else
+ return -1;
+}
diff --git a/sakisafecli/sakisafecli.h b/sakisafecli/sakisafecli.h
new file mode 100644
index 0000000..6b17f60
--- /dev/null
+++ b/sakisafecli/sakisafecli.h
@@ -0,0 +1,37 @@
+#ifndef SAKISAFECLI_H
+#define SAKISAFECLI_H
+#include <stdlib.h>
+#include <stdio.h>
+#include <curl/curl.h>
+
+struct progress
+{
+ char *_private;
+ size_t size;
+};
+
+size_t
+write_data(void *buffer, size_t size, size_t nmemb, void *userp);
+
+void
+print_usage();
+
+int
+store_link(const char *path, const char *buf);
+
+void
+print_help();
+
+size_t
+progress(void *clientp,
+ curl_off_t dltotal,
+ curl_off_t dlnow,
+ curl_off_t ultotal,
+ curl_off_t ulnow);
+
+void
+parse_config_file(FILE *config);
+int
+get_protocol(char *server);
+
+#endif /* SAKISAFECLI_H */
diff --git a/sakisafecli/sakisafeclirc.5 b/sakisafecli/sakisafeclirc.5
new file mode 100644
index 0000000..ab59920
--- /dev/null
+++ b/sakisafecli/sakisafeclirc.5
@@ -0,0 +1,73 @@
+.Dd $Mdocdate$
+.Dt sakisafeclirc 5
+.Os
+.Sh sakisafeclirc
+.Nm sakisafeclirc
+.Nd sakisafecli runtime configuration
+.Sh DESCRIPTION
+The sakisafecli(1) utility supports a configuration file. Which is by
+default in $HOME/.sakisafeclirc. This configuration file allows to
+change the default behaviour of sakisafecli(1) Lines that begin with
+.Sy #
+are ignored.
+
+The files uses a simple
+.Sy key=value
+format. This are the possible keys:
+
+.Sy server (string)
+Specify the server to upload the file.
+
+.Sy http_proxy (string)
+Specify the HTTP proxy address.
+
+.Sy socks_proxy (string)
+Specify the SOCKS proxy address.
+
+.Sy use_socks_sproxy (boolean)
+Specify whether to use or not the SOCKS Proxy specified with
+.Sy socks_proxy
+
+.Sy use_http_proxy (boolean)
+Specify whether to use or not the HTTP proxy specified with
+.Sy http_proxy
+
+.Sy force_ipv6 (boolean)
+Force an IPv6 connection. Cannot be used with
+.Sy force_ipv4
+
+.Sy force_ipv4 (boolean)
+Force an IPv4 connection. Cannot be used with
+.Sy force_ipv6
+
+.Sy key
+Path to the private ssh key used to connect to a server using the
+.Sy scp
+protocol.
+
+.Sh FILES
+
+.Bl -tag -width $HOME/.sakisafeclirc -compact
+.It Pa $HOME/.sakisafeclirc
+configuration file.
+
+.Sh EXAMPLE
+
+This example sets the default server to
+.Em https://ls.qorg11.net. Forcing
+.Em IPv6 connection
+and with the
+.Em socks_proxy
+127.0.0.1:9050
+
+.Bd -literal -offset indent;
+server="https://ss.suragu.net"
+socks_proxy="127.0.0.1:9050"
+force_ipv4=true
+use_socks_proxy=true
+.Ed
+.Sh AUTHORS
+Raoul Vaughn <chief_keef at riseup dot net>
+.Sh SEE ALSO
+sakisafecli(1)
+