Merge pull request #10203 from ynezz/upstream/19.07/cgi-io-backports
[openwrt-19.07] cgi-io backports
This commit is contained in:
commit
c3fb7d3a7d
3 changed files with 192 additions and 35 deletions
|
@ -8,7 +8,7 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=cgi-io
|
PKG_NAME:=cgi-io
|
||||||
PKG_RELEASE:=7
|
PKG_RELEASE:=14
|
||||||
|
|
||||||
PKG_LICENSE:=GPL-2.0-or-later
|
PKG_LICENSE:=GPL-2.0-or-later
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ define Package/cgi-io/install
|
||||||
$(INSTALL_DIR) $(1)/usr/libexec $(1)/www/cgi-bin/
|
$(INSTALL_DIR) $(1)/usr/libexec $(1)/www/cgi-bin/
|
||||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/cgi-io $(1)/usr/libexec
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/cgi-io $(1)/usr/libexec
|
||||||
$(LN) ../../usr/libexec/cgi-io $(1)/www/cgi-bin/cgi-upload
|
$(LN) ../../usr/libexec/cgi-io $(1)/www/cgi-bin/cgi-upload
|
||||||
|
$(LN) ../../usr/libexec/cgi-io $(1)/www/cgi-bin/cgi-download
|
||||||
$(LN) ../../usr/libexec/cgi-io $(1)/www/cgi-bin/cgi-backup
|
$(LN) ../../usr/libexec/cgi-io $(1)/www/cgi-bin/cgi-backup
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,12 @@ PROJECT(cgi-io C)
|
||||||
INCLUDE(CheckFunctionExists)
|
INCLUDE(CheckFunctionExists)
|
||||||
|
|
||||||
FIND_PATH(ubus_include_dir libubus.h)
|
FIND_PATH(ubus_include_dir libubus.h)
|
||||||
|
FIND_LIBRARY(ubox NAMES ubox)
|
||||||
|
FIND_LIBRARY(ubus NAMES ubus)
|
||||||
INCLUDE_DIRECTORIES(${ubus_include_dir})
|
INCLUDE_DIRECTORIES(${ubus_include_dir})
|
||||||
|
|
||||||
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations)
|
ADD_DEFINITIONS(-Os -Wall -Werror -Wextra --std=gnu99 -g3)
|
||||||
|
ADD_DEFINITIONS(-Wno-unused-parameter -Wmissing-declarations)
|
||||||
|
|
||||||
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
|
||||||
|
|
||||||
|
@ -17,6 +20,6 @@ IF(APPLE)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
ADD_EXECUTABLE(cgi-io main.c multipart_parser.c)
|
ADD_EXECUTABLE(cgi-io main.c multipart_parser.c)
|
||||||
TARGET_LINK_LIBRARIES(cgi-io ubox ubus)
|
TARGET_LINK_LIBRARIES(cgi-io ${ubox} ${ubus})
|
||||||
|
|
||||||
INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin)
|
INSTALL(TARGETS cgi-io RUNTIME DESTINATION sbin)
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE /* splice(), SPLICE_F_MORE */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
@ -26,12 +28,16 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/sendfile.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
|
||||||
#include <libubus.h>
|
#include <libubus.h>
|
||||||
#include <libubox/blobmsg.h>
|
#include <libubox/blobmsg.h>
|
||||||
|
|
||||||
#include "multipart_parser.h"
|
#include "multipart_parser.h"
|
||||||
|
|
||||||
|
#define READ_BLOCK 4096
|
||||||
|
|
||||||
enum part {
|
enum part {
|
||||||
PART_UNKNOWN,
|
PART_UNKNOWN,
|
||||||
|
@ -89,7 +95,7 @@ session_access_cb(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
session_access(const char *sid, const char *obj, const char *func)
|
session_access(const char *sid, const char *scope, const char *obj, const char *func)
|
||||||
{
|
{
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
bool allow = false;
|
bool allow = false;
|
||||||
|
@ -103,7 +109,7 @@ session_access(const char *sid, const char *obj, const char *func)
|
||||||
|
|
||||||
blob_buf_init(&req, 0);
|
blob_buf_init(&req, 0);
|
||||||
blobmsg_add_string(&req, "ubus_rpc_session", sid);
|
blobmsg_add_string(&req, "ubus_rpc_session", sid);
|
||||||
blobmsg_add_string(&req, "scope", "cgi-io");
|
blobmsg_add_string(&req, "scope", scope);
|
||||||
blobmsg_add_string(&req, "object", obj);
|
blobmsg_add_string(&req, "object", obj);
|
||||||
blobmsg_add_string(&req, "function", func);
|
blobmsg_add_string(&req, "function", func);
|
||||||
|
|
||||||
|
@ -120,6 +126,7 @@ static char *
|
||||||
checksum(const char *applet, size_t sumlen, const char *file)
|
checksum(const char *applet, size_t sumlen, const char *file)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
int r;
|
||||||
int fds[2];
|
int fds[2];
|
||||||
static char chksum[65];
|
static char chksum[65];
|
||||||
|
|
||||||
|
@ -148,10 +155,14 @@ checksum(const char *applet, size_t sumlen, const char *file)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
memset(chksum, 0, sizeof(chksum));
|
memset(chksum, 0, sizeof(chksum));
|
||||||
read(fds[0], chksum, sumlen);
|
r = read(fds[0], chksum, sumlen);
|
||||||
|
|
||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chksum;
|
return chksum;
|
||||||
|
@ -263,6 +274,64 @@ postdecode(char **fields, int n_fields)
|
||||||
return (found >= n_fields);
|
return (found >= n_fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
canonicalize_path(const char *path, size_t len)
|
||||||
|
{
|
||||||
|
char *canonpath, *cp;
|
||||||
|
const char *p, *e;
|
||||||
|
|
||||||
|
if (path == NULL || *path == '\0')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
canonpath = datadup(path, len);
|
||||||
|
|
||||||
|
if (canonpath == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* normalize */
|
||||||
|
for (cp = canonpath, p = path, e = path + len; p < e; ) {
|
||||||
|
if (*p != '/')
|
||||||
|
goto next;
|
||||||
|
|
||||||
|
/* skip repeating / */
|
||||||
|
if ((p + 1 < e) && (p[1] == '/')) {
|
||||||
|
p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /./ or /../ */
|
||||||
|
if ((p + 1 < e) && (p[1] == '.')) {
|
||||||
|
/* skip /./ */
|
||||||
|
if ((p + 2 >= e) || (p[2] == '/')) {
|
||||||
|
p += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* collapse /x/../ */
|
||||||
|
if ((p + 2 < e) && (p[2] == '.') && ((p + 3 >= e) || (p[3] == '/'))) {
|
||||||
|
while ((cp > canonpath) && (*--cp != '/'))
|
||||||
|
;
|
||||||
|
|
||||||
|
p += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
*cp++ = *p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove trailing slash if not root / */
|
||||||
|
if ((cp > canonpath + 1) && (cp[-1] == '/'))
|
||||||
|
cp--;
|
||||||
|
else if (cp == canonpath)
|
||||||
|
*cp++ = '/';
|
||||||
|
|
||||||
|
*cp = '\0';
|
||||||
|
|
||||||
|
return canonpath;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
response(bool success, const char *message)
|
response(bool success, const char *message)
|
||||||
{
|
{
|
||||||
|
@ -308,15 +377,17 @@ response(bool success, const char *message)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
failure(int e, const char *message)
|
failure(int code, int e, const char *message)
|
||||||
{
|
{
|
||||||
printf("Status: 500 Internal Server failure\r\n");
|
printf("Status: %d %s\r\n", code, message);
|
||||||
printf("Content-Type: text/plain\r\n\r\n");
|
printf("Content-Type: text/plain\r\n\r\n");
|
||||||
printf("%s", message);
|
printf("%s", message);
|
||||||
|
|
||||||
if (e)
|
if (e)
|
||||||
printf(": %s", strerror(e));
|
printf(": %s", strerror(e));
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,7 +395,7 @@ static int
|
||||||
filecopy(void)
|
filecopy(void)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
char buf[4096];
|
char buf[READ_BLOCK];
|
||||||
|
|
||||||
if (!st.filedata)
|
if (!st.filedata)
|
||||||
{
|
{
|
||||||
|
@ -376,7 +447,7 @@ header_field(multipart_parser *p, const char *data, size_t len)
|
||||||
static int
|
static int
|
||||||
header_value(multipart_parser *p, const char *data, size_t len)
|
header_value(multipart_parser *p, const char *data, size_t len)
|
||||||
{
|
{
|
||||||
int i, j;
|
size_t i, j;
|
||||||
|
|
||||||
if (!st.is_content_disposition)
|
if (!st.is_content_disposition)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -417,6 +488,9 @@ data_begin_cb(multipart_parser *p)
|
||||||
if (!st.filename)
|
if (!st.filename)
|
||||||
return response(false, "File data without name");
|
return response(false, "File data without name");
|
||||||
|
|
||||||
|
if (!session_access(st.sessionid, "file", st.filename, "write"))
|
||||||
|
return response(false, "Access to path denied by ACL");
|
||||||
|
|
||||||
st.tempfd = mkstemp(tmpname);
|
st.tempfd = mkstemp(tmpname);
|
||||||
|
|
||||||
if (st.tempfd < 0)
|
if (st.tempfd < 0)
|
||||||
|
@ -431,6 +505,8 @@ data_begin_cb(multipart_parser *p)
|
||||||
static int
|
static int
|
||||||
data_cb(multipart_parser *p, const char *data, size_t len)
|
data_cb(multipart_parser *p, const char *data, size_t len)
|
||||||
{
|
{
|
||||||
|
int wlen = len;
|
||||||
|
|
||||||
switch (st.parttype)
|
switch (st.parttype)
|
||||||
{
|
{
|
||||||
case PART_SESSIONID:
|
case PART_SESSIONID:
|
||||||
|
@ -438,7 +514,7 @@ data_cb(multipart_parser *p, const char *data, size_t len)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PART_FILENAME:
|
case PART_FILENAME:
|
||||||
st.filename = datadup(data, len);
|
st.filename = canonicalize_path(data, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PART_FILEMODE:
|
case PART_FILEMODE:
|
||||||
|
@ -446,14 +522,14 @@ data_cb(multipart_parser *p, const char *data, size_t len)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PART_FILEDATA:
|
case PART_FILEDATA:
|
||||||
if (write(st.tempfd, data, len) != len)
|
if (write(st.tempfd, data, len) != wlen)
|
||||||
{
|
{
|
||||||
close(st.tempfd);
|
close(st.tempfd);
|
||||||
return response(false, "I/O failure while writing temporary file");
|
return response(false, "I/O failure while writing temporary file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!st.filedata)
|
if (!st.filedata)
|
||||||
st.filedata = !!len;
|
st.filedata = !!wlen;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -469,7 +545,7 @@ data_end_cb(multipart_parser *p)
|
||||||
{
|
{
|
||||||
if (st.parttype == PART_SESSIONID)
|
if (st.parttype == PART_SESSIONID)
|
||||||
{
|
{
|
||||||
if (!session_access(st.sessionid, "upload", "write"))
|
if (!session_access(st.sessionid, "cgi-io", "upload", "write"))
|
||||||
{
|
{
|
||||||
errno = EPERM;
|
errno = EPERM;
|
||||||
return response(false, "Upload permission denied");
|
return response(false, "Upload permission denied");
|
||||||
|
@ -557,7 +633,8 @@ static int
|
||||||
main_upload(int argc, char *argv[])
|
main_upload(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int rem, len;
|
int rem, len;
|
||||||
char buf[4096];
|
bool done = false;
|
||||||
|
char buf[READ_BLOCK];
|
||||||
multipart_parser *p;
|
multipart_parser *p;
|
||||||
|
|
||||||
p = init_parser();
|
p = init_parser();
|
||||||
|
@ -570,16 +647,91 @@ main_upload(int argc, char *argv[])
|
||||||
|
|
||||||
while ((len = read(0, buf, sizeof(buf))) > 0)
|
while ((len = read(0, buf, sizeof(buf))) > 0)
|
||||||
{
|
{
|
||||||
|
if (!done) {
|
||||||
rem = multipart_parser_execute(p, buf, len);
|
rem = multipart_parser_execute(p, buf, len);
|
||||||
|
done = (rem < len);
|
||||||
if (rem < len)
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
multipart_parser_free(p);
|
multipart_parser_free(p);
|
||||||
|
|
||||||
/* read remaining post data */
|
return 0;
|
||||||
while ((len = read(0, buf, sizeof(buf))) > 0);
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
main_download(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *fields[] = { "sessionid", NULL, "path", NULL, "filename", NULL, "mimetype", NULL };
|
||||||
|
unsigned long long size = 0;
|
||||||
|
char *p, buf[READ_BLOCK];
|
||||||
|
ssize_t len = 0;
|
||||||
|
struct stat s;
|
||||||
|
int rfd;
|
||||||
|
|
||||||
|
postdecode(fields, 4);
|
||||||
|
|
||||||
|
if (!fields[1] || !session_access(fields[1], "cgi-io", "download", "read"))
|
||||||
|
return failure(403, 0, "Download permission denied");
|
||||||
|
|
||||||
|
if (!fields[3] || !session_access(fields[1], "file", fields[3], "read"))
|
||||||
|
return failure(403, 0, "Access to path denied by ACL");
|
||||||
|
|
||||||
|
if (stat(fields[3], &s))
|
||||||
|
return failure(404, errno, "Failed to stat requested path");
|
||||||
|
|
||||||
|
if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode))
|
||||||
|
return failure(403, 0, "Requested path is not a regular file or block device");
|
||||||
|
|
||||||
|
for (p = fields[5]; p && *p; p++)
|
||||||
|
if (!isalnum(*p) && !strchr(" ()<>@,;:[]?.=%-", *p))
|
||||||
|
return failure(400, 0, "Invalid characters in filename");
|
||||||
|
|
||||||
|
for (p = fields[7]; p && *p; p++)
|
||||||
|
if (!isalnum(*p) && !strchr(" .;=/-", *p))
|
||||||
|
return failure(400, 0, "Invalid characters in mimetype");
|
||||||
|
|
||||||
|
rfd = open(fields[3], O_RDONLY);
|
||||||
|
|
||||||
|
if (rfd < 0)
|
||||||
|
return failure(500, errno, "Failed to open requested path");
|
||||||
|
|
||||||
|
if (S_ISBLK(s.st_mode))
|
||||||
|
ioctl(rfd, BLKGETSIZE64, &size);
|
||||||
|
else
|
||||||
|
size = (unsigned long long)s.st_size;
|
||||||
|
|
||||||
|
printf("Status: 200 OK\r\n");
|
||||||
|
printf("Content-Type: %s\r\n", fields[7] ? fields[7] : "application/octet-stream");
|
||||||
|
|
||||||
|
if (fields[5])
|
||||||
|
printf("Content-Disposition: attachment; filename=\"%s\"\r\n", fields[5]);
|
||||||
|
|
||||||
|
printf("Content-Length: %llu\r\n\r\n", size);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
while (size > 0) {
|
||||||
|
len = sendfile(1, rfd, NULL, size);
|
||||||
|
|
||||||
|
if (len == -1) {
|
||||||
|
if (errno == ENOSYS || errno == EINVAL) {
|
||||||
|
while ((len = read(rfd, buf, sizeof(buf))) > 0)
|
||||||
|
fwrite(buf, len, 1, stdout);
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno == EINTR || errno == EAGAIN)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
size -= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(rfd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -589,24 +741,24 @@ main_backup(int argc, char **argv)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
time_t now;
|
time_t now;
|
||||||
|
int r;
|
||||||
int len;
|
int len;
|
||||||
int status;
|
int status;
|
||||||
int fds[2];
|
int fds[2];
|
||||||
char buf[4096];
|
|
||||||
char datestr[16] = { 0 };
|
char datestr[16] = { 0 };
|
||||||
char hostname[64] = { 0 };
|
char hostname[64] = { 0 };
|
||||||
char *fields[] = { "sessionid", NULL };
|
char *fields[] = { "sessionid", NULL };
|
||||||
|
|
||||||
if (!postdecode(fields, 1) || !session_access(fields[1], "backup", "read"))
|
if (!postdecode(fields, 1) || !session_access(fields[1], "cgi-io", "backup", "read"))
|
||||||
return failure(0, "Backup permission denied");
|
return failure(403, 0, "Backup permission denied");
|
||||||
|
|
||||||
if (pipe(fds))
|
if (pipe(fds))
|
||||||
return failure(errno, "Failed to spawn pipe");
|
return failure(500, errno, "Failed to spawn pipe");
|
||||||
|
|
||||||
switch ((pid = fork()))
|
switch ((pid = fork()))
|
||||||
{
|
{
|
||||||
case -1:
|
case -1:
|
||||||
return failure(errno, "Failed to fork process");
|
return failure(500, errno, "Failed to fork process");
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
dup2(fds[1], 1);
|
dup2(fds[1], 1);
|
||||||
|
@ -616,7 +768,9 @@ main_backup(int argc, char **argv)
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
|
|
||||||
chdir("/");
|
r = chdir("/");
|
||||||
|
if (r < 0)
|
||||||
|
return failure(500, errno, "Failed chdir('/')");
|
||||||
|
|
||||||
execl("/sbin/sysupgrade", "/sbin/sysupgrade",
|
execl("/sbin/sysupgrade", "/sbin/sysupgrade",
|
||||||
"--create-backup", "-", NULL);
|
"--create-backup", "-", NULL);
|
||||||
|
@ -624,7 +778,6 @@ main_backup(int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
|
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
strftime(datestr, sizeof(datestr) - 1, "%Y-%m-%d", localtime(&now));
|
strftime(datestr, sizeof(datestr) - 1, "%Y-%m-%d", localtime(&now));
|
||||||
|
|
||||||
|
@ -636,15 +789,13 @@ main_backup(int argc, char **argv)
|
||||||
printf("Content-Disposition: attachment; "
|
printf("Content-Disposition: attachment; "
|
||||||
"filename=\"backup-%s-%s.tar.gz\"\r\n\r\n", hostname, datestr);
|
"filename=\"backup-%s-%s.tar.gz\"\r\n\r\n", hostname, datestr);
|
||||||
|
|
||||||
do {
|
|
||||||
waitpid(pid, &status, 0);
|
|
||||||
|
|
||||||
while ((len = read(fds[0], buf, sizeof(buf))) > 0) {
|
|
||||||
fwrite(buf, len, 1, stdout);
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
|
||||||
|
|
||||||
} while (!WIFEXITED(status));
|
do {
|
||||||
|
len = splice(fds[0], NULL, 1, NULL, READ_BLOCK, SPLICE_F_MORE);
|
||||||
|
} while (len > 0);
|
||||||
|
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
|
@ -657,6 +808,8 @@ int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (strstr(argv[0], "cgi-upload"))
|
if (strstr(argv[0], "cgi-upload"))
|
||||||
return main_upload(argc, argv);
|
return main_upload(argc, argv);
|
||||||
|
else if (strstr(argv[0], "cgi-download"))
|
||||||
|
return main_download(argc, argv);
|
||||||
else if (strstr(argv[0], "cgi-backup"))
|
else if (strstr(argv[0], "cgi-backup"))
|
||||||
return main_backup(argc, argv);
|
return main_backup(argc, argv);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue