mirror of
https://github.com/phantomptr/ps5upload.git
synced 2026-10-19 02:20:02 -05:00
Upload, install, convert and manage PS5 game packages over the network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
3.2 KiB
C
108 lines
3.2 KiB
C
#include "pathsafe.h"
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
/* True if the path contains a ".." component (bounded by '/' or the ends). */
|
|
static int has_dotdot_component(const char *s) {
|
|
const char *p = s;
|
|
while (*p) {
|
|
if (p[0] == '.' && p[1] == '.' &&
|
|
(p[2] == '/' || p[2] == '\0') &&
|
|
(p == s || p[-1] == '/')) {
|
|
return 1;
|
|
}
|
|
p++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* True if `s` begins with "/mnt/usb" or "/mnt/ext" followed by >=1 digit and
|
|
* then a '/'. */
|
|
static int under_mnt_dev(const char *s) {
|
|
const char *tail;
|
|
if (strncmp(s, "/mnt/usb", 8) == 0) tail = s + 8;
|
|
else if (strncmp(s, "/mnt/ext", 8) == 0) tail = s + 8;
|
|
else return 0;
|
|
if (*tail < '0' || *tail > '9') return 0;
|
|
while (*tail >= '0' && *tail <= '9') tail++;
|
|
return *tail == '/';
|
|
}
|
|
|
|
static int ends_with_pkg(const char *s, size_t n) {
|
|
if (n < 4) return 0;
|
|
const char *e = s + n - 4;
|
|
return e[0] == '.' &&
|
|
(e[1] == 'p' || e[1] == 'P') &&
|
|
(e[2] == 'k' || e[2] == 'K') &&
|
|
(e[3] == 'g' || e[3] == 'G');
|
|
}
|
|
|
|
int inst_path_is_safe(const char *path) {
|
|
if (path == NULL || path[0] != '/') return 0;
|
|
size_t n = strlen(path);
|
|
if (n == 0 || n >= INST_PATH_MAX_SAFE) return 0;
|
|
if (path[n - 1] == '/') return 0; /* no trailing slash */
|
|
if (has_dotdot_component(path)) return 0;
|
|
if (!ends_with_pkg(path, n)) return 0;
|
|
if (strncmp(path, "/data/", 6) == 0) return 1;
|
|
if (strncmp(path, "/user/", 6) == 0) return 1;
|
|
if (under_mnt_dev(path)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int inst_rewrite_path(const char *in, char *out, size_t cap) {
|
|
int n;
|
|
if (strncmp(in, "/data/", 6) == 0) {
|
|
n = snprintf(out, cap, "/user%s", in);
|
|
} else {
|
|
n = snprintf(out, cap, "%s", in);
|
|
}
|
|
if (n < 0 || (size_t)n >= cap) return -1;
|
|
return 0;
|
|
}
|
|
|
|
const char *inst_basename(const char *path) {
|
|
const char *slash = strrchr(path, '/');
|
|
return slash ? slash + 1 : path;
|
|
}
|
|
|
|
static int is_unreserved(unsigned char c) {
|
|
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c == '-' || c == '_' || c == '.' || c == '~';
|
|
}
|
|
|
|
int inst_percent_encode(const char *in, char *out, size_t cap) {
|
|
static const char hex[] = "0123456789ABCDEF";
|
|
size_t o = 0;
|
|
for (const unsigned char *p = (const unsigned char *)in; *p; p++) {
|
|
if (is_unreserved(*p)) {
|
|
if (o + 1 >= cap) return -1;
|
|
out[o++] = (char)*p;
|
|
} else {
|
|
if (o + 3 >= cap) return -1;
|
|
out[o++] = '%';
|
|
out[o++] = hex[*p >> 4];
|
|
out[o++] = hex[*p & 0xF];
|
|
}
|
|
}
|
|
if (o >= cap) return -1;
|
|
out[o] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
int inst_build_loopback_url(char *out, size_t cap, uint16_t port,
|
|
const char *attempt, const char *base) {
|
|
char enc[INST_PATH_MAX_SAFE * 3];
|
|
if (inst_percent_encode(base, enc, sizeof(enc)) != 0) return -1;
|
|
int n = snprintf(out, cap, "http://127.0.0.1:%u/%s/%s",
|
|
(unsigned)port, attempt, enc);
|
|
if (n < 0 || (size_t)n >= cap) return -1;
|
|
return n;
|
|
}
|
|
|
|
int inst_is_network_class(uint32_t code) {
|
|
return (code & 0xFFFF0000u) == 0x80430000u ? 1 : 0;
|
|
}
|