220 lines
6.3 KiB
C
220 lines
6.3 KiB
C
/*
|
|
webdav-gtk A WebDAV client written with gtk-3.0 and libneon
|
|
Copyright (C) 2022 Xavier Del Campo
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <backend.h>
|
|
#include <neon/ne_auth.h>
|
|
#include <neon/ne_basic.h>
|
|
#include <neon/ne_locks.h>
|
|
#include <neon/ne_session.h>
|
|
#include <neon/ne_socket.h>
|
|
#include <neon/ne_uri.h>
|
|
#include <readline/readline.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static const char *nestrerror(const int error)
|
|
{
|
|
static const char *const s[] =
|
|
{
|
|
[NE_OK] = "Success",
|
|
[NE_ERROR] = "Generic error; use ne_get_error(session) for message",
|
|
[NE_LOOKUP] = "Server or proxy hostname lookup failed",
|
|
[NE_AUTH] = "User authentication failed on server",
|
|
[NE_PROXYAUTH] = "User authentication failed on proxy",
|
|
[NE_CONNECT] = "Could not connect to server",
|
|
[NE_TIMEOUT] = "Connection timed out",
|
|
[NE_FAILED] = "The precondition failed",
|
|
[NE_RETRY] = "Retry request (ne_end_request ONLY)",
|
|
[NE_REDIRECT] = "See ne_redirect.h"
|
|
};
|
|
|
|
if (error > 0 && error < sizeof s / sizeof *s)
|
|
return s[error];
|
|
|
|
return "unknown error";
|
|
}
|
|
|
|
static void notifier(void *const userdata, const ne_session_status status,
|
|
const ne_session_status_info *const info)
|
|
{
|
|
const struct backend_cfg *const cfg = userdata;
|
|
char buf[128];
|
|
|
|
switch (status)
|
|
{
|
|
case ne_status_lookup:
|
|
snprintf(buf, sizeof buf, "looking up hostname %s\n",
|
|
info->lu.hostname);
|
|
break;
|
|
case ne_status_connecting:
|
|
snprintf(buf, sizeof buf, "connecting to host %s\n", info->ci.hostname);
|
|
break;
|
|
case ne_status_connected:
|
|
snprintf(buf, sizeof buf, "connected to host %s\n", info->cd.hostname);
|
|
break;
|
|
case ne_status_sending:
|
|
if (info->sr.total != -1)
|
|
snprintf(buf, sizeof buf, "sending a request body [%ld/%ld]\n",
|
|
info->sr.progress, info->sr.total);
|
|
else
|
|
snprintf(buf, sizeof buf, "sending a request body (unknown size)\n");
|
|
|
|
break;
|
|
case ne_status_recving:
|
|
if (info->sr.total != -1)
|
|
snprintf(buf, sizeof buf, "receiving a response body [%ld/%ld]\n",
|
|
info->sr.progress, info->sr.total);
|
|
else
|
|
snprintf(buf, sizeof buf, "receiving a request body (unknown size)\n");
|
|
|
|
break;
|
|
case ne_status_disconnected:
|
|
snprintf(buf, sizeof buf, "disconnected from host\n");
|
|
break;
|
|
}
|
|
|
|
if (cfg->conn_status)
|
|
cfg->conn_status(buf, cfg->user_data);
|
|
}
|
|
|
|
static int server_auth(void *const userdata, const char *const realm,
|
|
const int attempt, char *const username, char *const password)
|
|
{
|
|
const struct backend_cfg *const cfg = userdata;
|
|
|
|
printf("%s: realm: %s, attempt: %d\n", __func__, realm, attempt);
|
|
const size_t ulen = strlen(cfg->username), plen = strlen(cfg->password);
|
|
|
|
if (ulen >= NE_ABUFSIZ)
|
|
{
|
|
fprintf(stderr, "username exceeds maximum size (%zu/%d bytes)\n",
|
|
ulen, NE_ABUFSIZ);
|
|
return -1;
|
|
}
|
|
else if (plen >= NE_ABUFSIZ)
|
|
{
|
|
fprintf(stderr, "password exceeds maximum size (%zu/%d bytes)\n",
|
|
plen, NE_ABUFSIZ);
|
|
return -1;
|
|
}
|
|
|
|
strcpy(username, cfg->username);
|
|
strcpy(password, cfg->password);
|
|
return 0;
|
|
}
|
|
|
|
static int format_uri(char *buf, const size_t n, const struct backend_cfg *const cfg)
|
|
{
|
|
#define COMMON_FMT "://%s/remote.php/dav/files/%s"
|
|
static const char https_fmt[] = "https" COMMON_FMT,
|
|
http_fmt[] = "http" COMMON_FMT;
|
|
#undef COMMON_FMT
|
|
int ret;
|
|
|
|
if (cfg->use_encryption)
|
|
ret = snprintf(buf, n, https_fmt,
|
|
cfg->hostname, cfg->username);
|
|
else
|
|
ret = snprintf(buf, n, http_fmt,
|
|
cfg->hostname, cfg->username);
|
|
|
|
return ret;
|
|
}
|
|
|
|
backend_session backend_connect(const struct backend_cfg *const cfg, const char **error)
|
|
{
|
|
int result;
|
|
ne_session *s = NULL;
|
|
ne_uri uri;
|
|
char *buf = NULL;
|
|
int n = format_uri(NULL, 0, cfg);
|
|
|
|
*error = NULL;
|
|
|
|
if (n < 0)
|
|
{
|
|
fprintf(stderr, "%s: fprintf(3) failed\n", __func__);
|
|
goto failure;
|
|
}
|
|
else if (!(buf = malloc(sizeof *buf * ++n)))
|
|
{
|
|
fprintf(stderr, "%s: failed to allocate URI: %s\n", __func__,
|
|
strerror(errno));
|
|
goto failure;
|
|
}
|
|
|
|
format_uri(buf, n, cfg);
|
|
|
|
if ((result = ne_uri_parse(buf, &uri)) != NE_OK)
|
|
{
|
|
fprintf(stderr, "ne_uri_parse failed: %d\n", result);
|
|
goto failure;
|
|
}
|
|
else if (!uri.host)
|
|
{
|
|
fprintf(stderr, "expected non-NULL uri.host\n");
|
|
goto failure;
|
|
}
|
|
else if (!(uri.port = ne_uri_defaultport(uri.scheme)))
|
|
{
|
|
fprintf(stderr, "ne_uri_defaultport failed\n");
|
|
goto failure;
|
|
}
|
|
else if (!(s = ne_session_create(uri.scheme, uri.host, uri.port)))
|
|
{
|
|
fprintf(stderr, "ne_session_create failed\n");
|
|
goto failure;
|
|
}
|
|
|
|
ne_set_notifier(s, notifier, (void *)cfg);
|
|
|
|
ne_lock_store *const store = ne_lockstore_create();
|
|
|
|
ne_lockstore_register(store, s);
|
|
ne_set_useragent(s, "gtk-webdav/0.1.0");
|
|
ne_set_server_auth(s, server_auth, (void *)cfg);
|
|
|
|
if (cfg->use_encryption)
|
|
ne_ssl_trust_default_ca(s);
|
|
|
|
unsigned int caps;
|
|
|
|
if ((result = ne_options2(s, uri.path, &caps)) != NE_OK)
|
|
{
|
|
if (result == NE_ERROR)
|
|
fprintf(stderr, "ne_options2 failed: %s\n", *error = ne_get_error(s));
|
|
else
|
|
fprintf(stderr, "ne_options2 failed: %s\n", *error = nestrerror(result));
|
|
|
|
goto failure;
|
|
}
|
|
|
|
free(buf);
|
|
return s;
|
|
|
|
failure:
|
|
if (s)
|
|
ne_session_destroy(s);
|
|
|
|
free(buf);
|
|
return NULL;
|
|
}
|