/* 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include 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; }