3proxy/src/plugins/FilePlugin/FilePlugin.c
Vladimir Dubrovin fa3807c6f1 Builds: no Open Watcom, no build without IPv6, no select()
The Win32 "lite" build with Open Watcom (Makefile.watcom, its release
workflow, the CMake branch for it) was the only one built without IPv6
(NOIPV6), without fibers and event loops, with a select() of its own
for poll() (mypoll()), and with the calling conventions of Watcom's
pragmas in the plugins. They are gone with it: every build has IPv6,
and polls with poll() on Unix and WSAPoll() on Windows, which were
what every other build asked for (WITH_POLL, WITH_WSAPOLL, the
3PROXY_USE_POLL and 3PROXY_USE_WSAPOLL options of CMake, FD_SETSIZE
for select()). getip(), the IPv4-only resolver of NOIPV6, goes too.

Plugin binary compatibility: plugins built with Open Watcom, and their
calling convention, are no longer supported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:54:51 +03:00

940 lines
26 KiB
C

/*
(c) 2007-2026 by Vladimir Dubrovin <vlad@3proxy.org>
please read License Agreement
*/
#include "../../structures.h"
#include "FilePlugin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/timeb.h>
#include <fcntl.h>
#include <time.h>
#ifdef _WIN32
#ifndef _WINCE
#include <io.h>
#else
#include <sys/unistd.h>
#endif
#else
#include <unistd.h>
#ifndef O_BINARY
#define O_BINARY (0)
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifndef _WIN32
#define WINAPI
#define fp_size_t size_t
#define fp_ssize_t ssize_t
#else
#define fp_size_t int
#define fp_ssize_t int
#endif
static struct pluginlink * pl;
static _3proxy_mutex_t file_mutex;
unsigned long preview = 0;
char path[300];
static int counter = 0;
static char * fp_stringtable[] = {
/* 0 */ "HTTP/1.0 503 Service Unavailable\r\n"
"Proxy-Connection: close\r\n"
"Content-type: text/html; charset=us-ascii\r\n"
"\r\n"
"<html><head><title>503 Service Unavailable</title></head>\r\n"
"<body><h2>503 Service Unavailable</h2><h3>HTTP policy violation: you have no permission to perform this action. Please conatct helpdesk or Administrator.</h3></body></html>\r\n",
/* 1 */ "421 SMTP policy violation: you have no permission to perform this action. Please conatct helpdesk or Administrator.\r\n",
/* 2 */ "421 FTP policy violation: you have no permission to perform this action. Please conatct helpdesk or Administrator.\r\n",
NULL
};
enum states {
STATE_INITIAL = 0,
GOT_HTTP_REQUEST,
GOT_HTTP_CLI_HDR,
GOT_HTTP_SRV_HDR,
GOT_HTTP_CLI_HDR2,
GOT_HTTP_SRV_HDR2,
GOT_HTTP_CLIDATA,
GOT_HTTP_SRVDATA,
GOT_SMTP_REQ,
GOT_SMTP_DATA,
GOT_FTP_REQ,
GOT_FTP_CLIDATA,
GOT_FTP_SRVDATA,
FLUSH_DATA
};
struct fp_callback {
struct fp_callback *next;
FP_CALLBACK callback;
void * data;
int what;
int preview_size;
int max_size;
};
struct fp_stream {
struct fp_stream *next;
char * buf;
int state;
int what;
int needsrvconnect;
int preview_size;
long bufsize;
unsigned long clihdrwritten, clientwritten, clientsent, srvhdrwritten, serverwritten, serversent;
struct fp_callback *callbacks;
struct fp_filedata fpd;
} *fp_streams = NULL;
struct sockfuncs sso;
static void genpaths(struct fp_stream *fps){
size_t len = strlen(path) + 32;
if(fps->what & (FP_CLIDATA|FP_CLIHEADER)){
if(fps->fpd.path_cli) free(fps->fpd.path_cli);
fps->fpd.path_cli = malloc(len);
if(fps->fpd.path_cli) sprintf(fps->fpd.path_cli, "%s%07d.tmp", path, counter++);
}
if(fps->what & (FP_SRVDATA|FP_SRVHEADER)){
if(fps->fpd.path_srv) free(fps->fpd.path_srv);
fps->fpd.path_srv = malloc(len);
if(fps->fpd.path_srv) sprintf(fps->fpd.path_srv, "%s%07d.tmp", path, counter++);
}
}
static
#ifdef _WIN32
HANDLE
#else
int
#endif
initclientfile(struct fp_stream *fps){
fps->clientwritten = fps->clientsent = 0;
#ifdef _WIN32
if(fps->fpd.h_cli != INVALID_HANDLE_VALUE){
CloseHandle(fps->fpd.h_cli);
}
fps->fpd.h_cli = CreateFile(fps->fpd.path_cli, GENERIC_READ | GENERIC_WRITE, (fps->what & FP_SHAREFILE)? FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE:0, NULL, CREATE_ALWAYS, (fps->what & (FP_KEEPFILE|FP_SHAREFILE))? FILE_ATTRIBUTE_TEMPORARY : FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
return fps->fpd.h_cli;
#else
if(fps->fpd.fd_cli != -1) close(fps->fpd.fd_cli);
if(!fps->fpd.path_cli) return (fps->fpd.fd_cli = -1);
fps->fpd.fd_cli = open(fps->fpd.path_cli, O_BINARY|O_RDWR|O_CREAT|O_TRUNC, 0600);
return fps->fpd.fd_cli;
#endif
}
static
#ifdef _WIN32
HANDLE
#else
int
#endif
initserverfile(struct fp_stream *fps){
fps->serverwritten = fps->serversent = 0;
#ifdef _WIN32
if(fps->fpd.h_srv != INVALID_HANDLE_VALUE){
CloseHandle(fps->fpd.h_srv);
}
fps->fpd.h_srv = CreateFile(fps->fpd.path_srv, GENERIC_READ | GENERIC_WRITE, (fps->what & FP_SHAREFILE)? FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE:0, NULL, CREATE_ALWAYS, (fps->what & (FP_KEEPFILE|FP_SHAREFILE))? FILE_ATTRIBUTE_TEMPORARY : FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
return fps->fpd.h_srv;
#else
if(fps->fpd.fd_srv != -1) close(fps->fpd.fd_srv);
if(!fps->fpd.path_srv) return (fps->fpd.fd_srv = -1);
fps->fpd.fd_srv = open(fps->fpd.path_srv, O_BINARY|O_RDWR|O_CREAT|O_TRUNC, 0600);
return fps->fpd.fd_srv;
#endif
}
static void closefiles(struct fp_stream *fps){
#ifdef _WIN32
if(fps->fpd.h_cli != INVALID_HANDLE_VALUE) {
CloseHandle(fps->fpd.h_cli);
fps->fpd.h_cli = INVALID_HANDLE_VALUE;
if((fps->what & FP_SHAREFILE) && !(fps->what & FP_KEEPFILE)) DeleteFile(fps->fpd.path_cli);
}
if(fps->fpd.h_srv != INVALID_HANDLE_VALUE) {
CloseHandle(fps->fpd.h_srv);
fps->fpd.h_srv = INVALID_HANDLE_VALUE;
if((fps->what & FP_SHAREFILE) && !(fps->what & FP_KEEPFILE)) DeleteFile(fps->fpd.path_cli);
}
#else
if(fps->fpd.fd_cli != -1) {
close(fps->fpd.fd_cli);
fps->fpd.fd_cli = -1;
if(!(fps->what & FP_KEEPFILE)) unlink(fps->fpd.path_cli);
}
if(fps->fpd.fd_srv != -1) {
close(fps->fpd.fd_srv);
fps->fpd.fd_srv = -1;
if(!(fps->what & FP_KEEPFILE)) unlink(fps->fpd.path_srv);
}
#endif
if(fps->fpd.path_cli) {
free(fps->fpd.path_cli);
fps->fpd.path_cli = NULL;
}
if(fps->fpd.path_srv) {
free(fps->fpd.path_srv);
fps->fpd.path_srv = NULL;
}
fps->clihdrwritten = fps->clientwritten = fps->clientsent = fps->srvhdrwritten = fps->serverwritten = fps->serversent = 0;
}
static int searchsocket(SOCKET s, struct fp_stream **pfps){
struct fp_stream *fps = NULL;
int ret = 0;
_3proxy_mutex_lock(&file_mutex);
for(fps = fp_streams; fps; fps = fps->next){
if(fps->fpd.cp->clisock == s) {
ret = 1;
break;
}
if(fps->fpd.cp->remsock == s) {
ret = 2;
break;
}
if(fps->fpd.cp->ctrlsock == s) {
ret = 3;
break;
}
}
_3proxy_mutex_unlock(&file_mutex);
*pfps = fps;
return ret;
}
static void freecallback(struct fp_stream * fps, struct fp_callback * fpc){
if(fpc->next) freecallback(fps, fpc->next);
if(fpc->what & FP_CALLONREMOVE) (*fpc->callback)(FP_CALLONREMOVE, fpc->data, &fps->fpd, NULL, 0);
free(fpc);
}
static void removefps(struct fp_stream * fps){
if(!fp_streams) return;
_3proxy_mutex_lock(&file_mutex);
if(fp_streams == fps)fp_streams = fps->next;
else {
struct fp_stream *fps2;
for(fps2 = fp_streams; fps2->next; fps2 = fps2->next){
if(fps2->next == fps){
fps2->next = fps->next;
break;
}
}
}
_3proxy_mutex_unlock(&file_mutex);
if(fps->callbacks){
freecallback(fps, fps->callbacks);
fps->callbacks = 0;
}
closefiles(fps);
if(fps->buf) {
free(fps->buf);
fps->buf = NULL;
}
fps->state = 0;
}
void processcallbacks(struct fp_stream *fps, int what, char *msg, int size){
struct fp_callback *cb;
int state;
state = fps->state;
if(fps->what & what) {
fps->what = 0;
for(cb = fps->callbacks; cb; cb=cb->next){
if(cb->what & what){
cb->what = (*cb->callback)(what, cb->data, &(fps->fpd), msg, size);
}
fps->what |= cb->what;
}
}
if(fps->what & FP_REJECT){
switch(state){
/*
Fixme: handle different states
*/
case GOT_SMTP_REQ:
case GOT_SMTP_DATA:
fps->state = FLUSH_DATA;
pl->socksend(NULL, fps->fpd.cp->clisock, (unsigned char *)fp_stringtable[1], (int)strlen((char *)fp_stringtable[1]), fps->fpd.cp->srv->timeouts[STRING_S]);
fps->state = state;
break;
case GOT_HTTP_REQUEST:
case GOT_HTTP_CLI_HDR:
case GOT_HTTP_SRV_HDR:
case GOT_HTTP_CLI_HDR2:
case GOT_HTTP_SRV_HDR2:
case GOT_HTTP_CLIDATA:
case GOT_HTTP_SRVDATA:
if(!fps->serversent){
fps->state = FLUSH_DATA;
pl->socksend(NULL, fps->fpd.cp->clisock, (unsigned char *)fp_stringtable[0], (int)strlen((char *)fp_stringtable[0]), fps->fpd.cp->srv->timeouts[STRING_S]);
fps->state = state;
}
break;
case GOT_FTP_CLIDATA:
case GOT_FTP_REQ:
case GOT_FTP_SRVDATA:
fps->state = FLUSH_DATA;
pl->socksend(NULL, fps->fpd.cp->ctrlsock, (unsigned char *)fp_stringtable[1], (int)strlen((char *)fp_stringtable[1]), fps->fpd.cp->srv->timeouts[STRING_S]);
fps->state = state;
break;
default:
break;
}
if(fps->fpd.cp->remsock != INVALID_SOCKET)sso._closesocket(sso.state, fps->fpd.cp->remsock);
fps->fpd.cp->remsock = INVALID_SOCKET;
if(fps->fpd.cp->clisock != INVALID_SOCKET)sso._closesocket(sso.state, fps->fpd.cp->clisock);
fps->fpd.cp->clisock = INVALID_SOCKET;
}
}
static int copyfdtosock(struct fp_stream * fps, DIRECTION which, long len){
int res;
long toread;
int state;
#ifdef _WIN32
HANDLE h;
#else
int fd;
#endif
SOCKET sock;
long offset;
int sendchunk = 0;
state = fps->state;
fps->state = FLUSH_DATA;
if(!fps->buf){
fps->buf = malloc(2048);
if(!fps->buf) return -2;
fps->bufsize = 2048;
}
if(which == SERVER){
offset = fps->clientsent;
fps->clientsent += len;
#ifdef _WIN32
h = fps->fpd.h_cli;
#else
fd = fps->fpd.fd_cli;
#endif
sock = fps->fpd.cp->remsock;
}
else {
if(fps->fpd.cp->chunked){
if(fps->serversent < fps->srvhdrwritten && (fps->serversent + len) > fps->srvhdrwritten){
len -= fps->srvhdrwritten - fps->serversent;
if ((res = copyfdtosock(fps, which, fps->srvhdrwritten - fps->serversent))) return res;
}
if(fps->serversent >= fps->srvhdrwritten){
sprintf(fps->buf, "%lx\r\n", len);
sendchunk = (int)strlen(fps->buf);
if(pl->socksend(NULL, fps->fpd.cp->clisock, (unsigned char *)fps->buf, sendchunk, fps->fpd.cp->srv->timeouts[STRING_S]) != sendchunk){
return -4;
}
}
}
offset = fps->serversent;
fps->serversent += len;
#ifdef _WIN32
h = fps->fpd.h_srv;
#else
fd = fps->fpd.fd_srv;
#endif
sock = fps->fpd.cp->clisock;
}
#ifdef _WIN32
if(SetFilePointer(h,offset,0,FILE_BEGIN)!=offset){
return -1;
}
#else
if(lseek(fd, offset, SEEK_SET) < 0) {
return -1;
}
#endif
while(len > 0){
/*
Fixme: prevent client/server timeouts
*/
toread = (len > fps->bufsize)? fps->bufsize:len;
#ifdef _WIN32
if(!ReadFile(h, fps->buf, (DWORD)toread,(DWORD *)&res,NULL)) {
#else
if((res = read(fd, fps->buf, toread)) <= 0) {
#endif
return -3;
}
if(pl->socksend(NULL, sock, (unsigned char *)fps->buf, res, fps->fpd.cp->srv->timeouts[STRING_S]) != res) {
return -4;
}
len -= res;
}
if(sendchunk){
if(pl->socksend(NULL, sock, (unsigned char *)"\r\n", 2, fps->fpd.cp->srv->timeouts[STRING_S]) != 2)
return -4;
}
fps->state = state;
return 0;
}
#ifdef _WIN32
static int WINAPI fp_prepoll(void *state, struct pollfd *fds, unsigned int nfds){
#else
static int fp_prepoll(void *state, struct pollfd *fds, nfds_t nfds){
#endif
struct fp_stream *fps = NULL;
int res;
unsigned i;
for(i = 0; i<nfds; i++){
res = searchsocket(fds[i].fd, &fps);
if(res == 2 && fps->state == GOT_SMTP_DATA){
if(fds[i].events & POLLOUT){
fds[i].revents = POLLOUT;
return 1;
}
}
else if(res == 2 && (((fps->what & FP_CLIHEADER) && (fps->state == GOT_HTTP_REQUEST || fps->state == GOT_HTTP_CLI_HDR2)) || ((fps->what & FP_CLIDATA) && fps->state == GOT_HTTP_CLIDATA))){
if(fds[i].events & POLLIN){
processcallbacks(fps, (fps->state == GOT_HTTP_CLIDATA)?FP_CLIDATA:FP_CALLAFTERCLIHEADERS, NULL, 0);
if(fps->clihdrwritten + fps->clientwritten > fps->clientsent) {
if(copyfdtosock(fps, SERVER, (fps->clihdrwritten + fps->clientwritten) - fps->clientsent))
return -2;
}
if(fps->state) {
if(fps->what & FP_SRVHEADER) initserverfile(fps);
fps->state = GOT_HTTP_SRV_HDR;
}
}
else if(fds[i].events & POLLOUT){
fds[i].revents = POLLOUT;
return 1;
}
}
else if(res == 1 && (fps->state == GOT_HTTP_SRVDATA || fps->state == GOT_HTTP_SRV_HDR || fps->state == GOT_HTTP_SRV_HDR2)&& (fds[i].events & POLLIN)){
processcallbacks(fps, (fps->state == GOT_HTTP_SRVDATA)? FP_SRVDATA:FP_CALLAFTERSRVHEADERS, NULL, 0);
if(fps->srvhdrwritten + fps->serverwritten > fps->serversent) {
if(copyfdtosock(fps, CLIENT, (fps->srvhdrwritten + fps->serverwritten) - fps->serversent))
return -2;
}
closefiles(fps);
fps->state = 0;
}
}
return sso._prepoll(sso.state, fds, nfds);
}
#ifdef _WIN32
static int WINAPI fp_send(void *state, SOCKET s, const char *msg, int len, int flags){
#else
static fp_ssize_t fp_send(void *state, SOCKET s, const void *msg, size_t len, int flags){
#endif
struct fp_stream *fps = NULL;
int res;
res = searchsocket(s, &fps);
if(res == 2){
if(fps->state == GOT_SMTP_DATA) {
if(fps->clihdrwritten + fps->clientwritten > fps->clientsent) {
processcallbacks(fps, FP_CLIDATA, NULL, 0);
if(copyfdtosock(fps, SERVER, (fps->clihdrwritten + fps->clientwritten) - fps->clientsent)) {
return -1;
}
fps->state = 0;
}
closefiles(fps);
fps->state = 0;
return sso._send(sso.state, s, msg, len, flags);
}
if((((fps->what & FP_CLIHEADER) && (fps->state == GOT_HTTP_REQUEST || fps->state == GOT_HTTP_CLI_HDR2)) || ((fps->what & FP_CLIDATA) && fps->state == GOT_HTTP_CLIDATA))){
#ifdef _WIN32
if(SetFilePointer(fps->fpd.h_cli, fps->clientwritten + fps->clihdrwritten, 0, FILE_BEGIN) != (fps->clientwritten + fps->clihdrwritten)){
return -1;
}
if(!WriteFile(fps->fpd.h_cli, msg, (DWORD)len,(DWORD *)&res,NULL) || res != len){
return -1;
}
#else
if(lseek(fps->fpd.fd_cli, fps->clientwritten + fps->clihdrwritten, SEEK_SET) < 0) {
return -1;
}
if((res = write(fps->fpd.fd_cli, msg, len) != len)) return -1;
#endif
if(fps->state == GOT_HTTP_CLIDATA)fps->clientwritten += res;
else fps->clihdrwritten += res;
return res;
}
}
if(res == 1){
if(((fps->what & FP_SRVDATA) && (fps->state == GOT_HTTP_SRVDATA || fps->state == GOT_HTTP_SRV_HDR) && fps->fpd.cp->chunked && len < 16 )){
int hasnonzero = 0, i;
for(i=0; i < len; i++){
char c = ((char *)msg)[i];
if(c == '\r' || c == '\n') continue;
if((c<'0'|| c>'9') && (c<'A' || c>'F') && (c<'a' || c>'f')) {
return sso._send(sso.state, s, msg, len, flags);
}
if(c != '0') hasnonzero = 1;
}
if(i>2 && !hasnonzero){
if(fps->srvhdrwritten + fps->serverwritten > fps->serversent) {
processcallbacks(fps, FP_SRVDATA, NULL, 0);
if(copyfdtosock(fps, CLIENT, (fps->srvhdrwritten + fps->serverwritten) - fps->serversent)) {
return -1;
}
fps->state = 0;
}
closefiles(fps);
fps->state = 0;
return sso._send(sso.state, s, msg, len, flags);
}
return len;
}
if(((fps->what & FP_SRVHEADER) && (fps->state == GOT_HTTP_SRV_HDR || fps->state == GOT_HTTP_SRV_HDR2))){
#ifdef _WIN32
if(SetFilePointer(fps->fpd.h_srv, fps->serverwritten + fps->srvhdrwritten, 0, FILE_BEGIN) != (fps->serverwritten + fps->srvhdrwritten)){
return -1;
}
if(!WriteFile(fps->fpd.h_srv, msg, (DWORD)len,(DWORD *)&res,NULL) || res !=len){
return -1;
}
#else
if(lseek(fps->fpd.fd_srv, fps->serverwritten + fps->srvhdrwritten, SEEK_SET) < 0) {
return -1;
}
if((res = write(fps->fpd.fd_srv, msg, len) != len)) return -1;
#endif
fps->srvhdrwritten += res;
return res;
}
}
return sso._send(sso.state, s, msg, len, flags);
}
#ifdef _WIN32
static int WINAPI fp_sendto(void *state, SOCKET s, const char *msg, int len, int flags, const struct sockaddr *to, int tolen
#else
static fp_ssize_t fp_sendto(void *state, SOCKET s, const void *msg, fp_size_t len, int flags, const struct sockaddr *to, SASIZETYPE tolen
#endif
){
struct fp_stream *fps = NULL;
int res;
res = searchsocket(s, &fps);
if(res == 2) {
switch(fps->state){
case GOT_SMTP_REQ:
if(!(fps->what & FP_CLIDATA)) break;
fps->state = GOT_SMTP_DATA;
initclientfile(fps);
case GOT_FTP_REQ:
if(fps->state == GOT_FTP_REQ){
if(!(fps->what & FP_CLIDATA)) break;
fps->state = GOT_FTP_CLIDATA;
initclientfile(fps);
}
case GOT_HTTP_CLI_HDR2:
if(fps->state == GOT_HTTP_CLI_HDR2){
processcallbacks(fps, FP_CALLAFTERCLIHEADERS, NULL, 0);
if ((fps->what & FP_REJECT)) return -1;
if((fps->what & FP_CLIDATA) && !(fps->what & FP_CLIHEADER)) initclientfile(fps);
else if(!(fps->what & FP_CLIDATA) && (fps->what & FP_CLIHEADER)){
if(fps->clihdrwritten + fps->clientwritten > fps->clientsent) {
if(copyfdtosock(fps, SERVER, (fps->clihdrwritten + fps->clientwritten) - fps->clientsent))
return -2;
}
}
fps->state = GOT_HTTP_CLIDATA;
}
case GOT_HTTP_REQUEST:
if(fps->state == GOT_HTTP_REQUEST && !(fps->what & FP_CLIHEADER)) break;
case GOT_SMTP_DATA:
case GOT_FTP_CLIDATA:
case GOT_FTP_SRVDATA:
case GOT_HTTP_CLIDATA:
if(!(fps->what & FP_CLIDATA)) break;
#ifdef _WIN32
if(SetFilePointer(fps->fpd.h_cli, fps->clientwritten + fps->clihdrwritten, 0, FILE_BEGIN) != (fps->clientwritten + fps->clihdrwritten)){
return -1;
}
if(!WriteFile(fps->fpd.h_cli, msg, (DWORD)len,(DWORD *)&res,NULL) || res != len) {
return -1;
}
#else
if(lseek(fps->fpd.fd_cli, fps->clientwritten + fps->clihdrwritten, SEEK_SET) < 0) {
return -1;
}
if((res = write(fps->fpd.fd_cli, msg, len) != len)) return -1;
#endif
if(fps->state == GOT_HTTP_REQUEST)fps->clihdrwritten += res;
else fps->clientwritten += res;
if(fps->preview_size && ((fps->clihdrwritten + fps->clientwritten) > (fps->clientsent + fps->preview_size))){
if(!fps->clientsent){
processcallbacks(fps, FP_PREVIEWCLI, NULL, 0);
if ((fps->what & FP_REJECT)) return -1;
}
if(copyfdtosock(fps, SERVER, (fps->clihdrwritten + fps->clientwritten) - (fps->clientsent + fps->preview_size)))
return -1;
}
return res;
}
}
else if(res == 1){
switch(fps->state){
case GOT_HTTP_SRV_HDR2:
processcallbacks(fps, FP_CALLAFTERSRVHEADERS, NULL, 0);
if ((fps->what & FP_REJECT)) return REJECT;
if((fps->what & FP_SRVDATA) && !(fps->what & FP_SRVHEADER)) initserverfile(fps);
else if(!(fps->what & FP_SRVDATA) && (fps->what & FP_SRVHEADER)){
if(fps->srvhdrwritten + fps->serverwritten > fps->serversent) {
if(copyfdtosock(fps, CLIENT, (fps->srvhdrwritten + fps->serverwritten) - fps->serversent))
return -2;
}
}
fps->state = GOT_HTTP_SRVDATA;
case GOT_FTP_REQ:
if(fps->state == GOT_FTP_REQ){
if(!(fps->what & FP_SRVDATA)) break;
fps->state = GOT_FTP_SRVDATA;
initserverfile(fps);
}
case GOT_HTTP_SRV_HDR:
if(fps->state == GOT_HTTP_SRV_HDR && !(fps->what & FP_SRVHEADER)) break;
case GOT_HTTP_SRVDATA:
case GOT_FTP_SRVDATA:
case GOT_FTP_CLIDATA:
if(!(fps->what & FP_SRVDATA)) break;
#ifdef _WIN32
if(SetFilePointer(fps->fpd.h_srv, fps->serverwritten + fps->srvhdrwritten, 0, FILE_BEGIN) != (fps->serverwritten + fps->srvhdrwritten)){
return -1;
}
if(!WriteFile(fps->fpd.h_srv, msg, (DWORD)len,(DWORD *)&res,NULL) || res != len){
return -1;
}
#else
if(lseek(fps->fpd.fd_srv, fps->serverwritten + fps->srvhdrwritten, SEEK_SET) < 0) {
return -1;
}
if((res = write(fps->fpd.fd_srv, msg, len) != len)) return -1;
#endif
if(fps->state == GOT_HTTP_SRV_HDR)fps->srvhdrwritten += res;
else fps->serverwritten += res;
if(fps->preview_size && ((fps->srvhdrwritten + fps->serverwritten) > (fps->serversent + fps->preview_size))){
if(!fps->serversent){
processcallbacks(fps, FP_PREVIEWSRV, NULL, 0);
if ((fps->what & FP_REJECT)) return -1;
}
if(copyfdtosock(fps, CLIENT, (fps->srvhdrwritten + fps->serverwritten) - (fps->serversent + fps->preview_size)))
return -1;
}
return res;
}
}
return sso._sendto(sso.state, s, msg, len, flags, to, tolen);
}
#ifdef _WIN32
static int WINAPI fp_recv(void *state, SOCKET s, char *buf, int len, int flags
#else
static fp_ssize_t fp_recv(void *state, SOCKET s, void *buf, fp_size_t len, int flags
#endif
){
return sso._recv(sso.state, s, buf, len, flags);
}
#ifdef _WIN32
static int WINAPI fp_recvfrom(void *state, SOCKET s, char *buf, int len, int flags, struct sockaddr * from, int * fromlen
#else
static fp_ssize_t fp_recvfrom(void *state, SOCKET s, void *buf, fp_size_t len, int flags, struct sockaddr * from, SASIZETYPE * fromlen
#endif
){
return sso._recvfrom(sso.state, s, buf, len, flags, from, fromlen);
}
static int WINAPI fp_shutdown(void *state, SOCKET s, int how){
struct fp_stream *fps = NULL;
int res;
res = searchsocket(s, &fps);
if(res){
if(fps->state == GOT_HTTP_SRV_HDR || fps->state == GOT_HTTP_SRVDATA || fps->state == GOT_FTP_SRVDATA){
if(fps->srvhdrwritten + fps->serverwritten > fps->serversent) {
processcallbacks(fps, FP_SRVDATA, NULL, 0);
copyfdtosock(fps, CLIENT, (fps->srvhdrwritten + fps->serverwritten) - fps->serversent);
}
closefiles(fps);
fps->state = 0;
}
else if(fps->state == GOT_FTP_CLIDATA){
if(fps->clihdrwritten + fps->clientwritten > fps->clientsent) {
processcallbacks(fps, FP_CLIDATA, NULL, 0);
copyfdtosock(fps, SERVER, (fps->clihdrwritten + fps->clientwritten) - fps->clientsent);
}
closefiles(fps);
fps->state = 0;
}
}
return sso._shutdown(sso.state, s, how);
}
static int WINAPI fp_closesocket(void *state, SOCKET s){
return sso._closesocket(sso.state, s);
}
struct fp_stream * addfps(struct clientparam *cp){
struct fp_stream *fps;
for(fps = fp_streams; fps && fps->fpd.cp != cp; fps = fps->next);
if(!fps) {
fps = malloc(sizeof(struct fp_stream));
if(!fps){
return NULL;
}
memset(fps, 0, sizeof(struct fp_stream));
fps->fpd.cp = cp;
fps->next = fp_streams;
fp_streams = fps;
#ifdef _WIN32
fps->fpd.h_cli = fps->fpd.h_srv = INVALID_HANDLE_VALUE;
#else
fps->fpd.fd_cli = fps->fpd.fd_srv = -1;
#endif
}
return fps;
}
static int fp_registercallback (int what, int max_size, int preview_size, struct clientparam *cp, FP_CALLBACK cb, void *data){
struct fp_callback * fpc;
struct fp_stream *fps;
fpc = malloc(sizeof(struct fp_callback));
if(!fpc) return 0;
fpc->what = what;
fpc->preview_size = preview_size;
fpc->max_size = max_size;
fpc->data = data;
fpc->callback = cb;
_3proxy_mutex_lock(&file_mutex);
fps = addfps(cp);
if(fps){
fpc->next = fps->callbacks;
fps->callbacks = fpc;
fps->what |= fpc->what;
if(preview_size > fps->preview_size) fps->preview_size = preview_size;
}
else free(fpc);
_3proxy_mutex_unlock(&file_mutex);
return fps?1:0;
}
/* Its _prepoll writes the files and sends them: the service's clients each
have a thread. */
static void * fp_open(void * idata, struct srvparam * param){
param->threadperclient = 1;
return idata;
}
#define FC ((struct fp_stream *)fc)
static FILTER_ACTION fp_client(void *fo, struct clientparam * param, void** fc){
_3proxy_mutex_lock(&file_mutex);
(*fc) = (void *)addfps(param);
_3proxy_mutex_unlock(&file_mutex);
return CONTINUE;
}
static FILTER_ACTION fp_request(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
if(fc && (param->service == S_PROXY)){
if(FC->state) {
closefiles(FC);
FC->state = 0;
}
processcallbacks(FC, FP_CALLONREQUEST, (char *)*buf_p + offset, *length_p - offset);
if(FC->what &FP_REJECT) return REJECT;
FC->state = GOT_HTTP_REQUEST;
genpaths(FC);
if(FC->what & FP_CLIHEADER) initclientfile(FC);
}
return CONTINUE;
}
static FILTER_ACTION fp_hcli(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
if(fc && param->service == S_SMTPP) {
processcallbacks(FC, FP_CALLONREQUEST, (char *)*buf_p + offset, *length_p - offset);
if(FC->what & FP_REJECT) return REJECT;
if(!FC->state)genpaths(FC);
FC->state = GOT_SMTP_REQ;
}
if(fc && param->service == S_FTPPR) {
processcallbacks(FC, FP_CALLONREQUEST, (char *)*buf_p + offset, *length_p - offset);
if(FC->what & FP_REJECT) return REJECT;
genpaths(FC);
FC->state = GOT_FTP_REQ;
}
return CONTINUE;
}
static FILTER_ACTION fp_hsrv(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
if(fc && param->service == S_PROXY && (FC->state == GOT_HTTP_REQUEST || FC->state == GOT_HTTP_CLI_HDR || FC->state == GOT_HTTP_CLIDATA)){
if(FC->what & FP_SRVHEADER) initserverfile(FC);
FC->state = GOT_HTTP_SRV_HDR;
}
return CONTINUE;
}
static FILTER_ACTION fp_dcli(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
if(fc && FC->state == GOT_HTTP_REQUEST){
FC->state = GOT_HTTP_CLI_HDR2;
}
return CONTINUE;
}
static FILTER_ACTION fp_dsrv(void *fc, struct clientparam * param, unsigned char ** buf_p, int * bufsize_p, int offset, int * length_p){
if(fc && (FC->state == GOT_HTTP_REQUEST || FC->state == GOT_HTTP_CLI_HDR || FC->state == GOT_HTTP_CLIDATA || FC->state == GOT_HTTP_CLIDATA || FC->state == GOT_HTTP_SRV_HDR)){
FC->state = GOT_HTTP_SRV_HDR2;
}
return CONTINUE;
}
static void fp_clear(void *fc){
removefps(FC);
free(fc);
}
static void fp_close(void *fo){
}
static struct filter fp_filter = {
NULL,
"filefilter",
"filefilter",
fp_open,
fp_client,
fp_request,
NULL,
NULL,
fp_hcli,
fp_hsrv,
NULL,
fp_dcli,
fp_dsrv,
fp_clear,
fp_close,
};
static struct symbol fp_symbols[] = {
{fp_symbols + 1, "fp_registercallback", (void*) fp_registercallback},
{NULL, "fp_stringtable", (void*) fp_stringtable}
};
static int h_cachedir(int argc, unsigned char **argv){
char * dirp;
size_t len;
dirp = (argc > 1)? (char *)argv[1] : getenv("TEMP");
len = dirp? strlen(dirp) : 0;
if(!dirp || !len || len > 200 || strchr(dirp, '%')) {
fprintf(stderr, "FilePlugin: invalid directory path: %s\n", dirp);
return (1);
}
#ifdef _WIN32
if(dirp[len-1] == '\\') dirp[len-1] = 0;
sprintf(path, "%.256s\\", dirp);
#else
if(dirp[len-1] == '/') dirp[len-1] = 0;
sprintf(path, "%.256s/", dirp);
#endif
return 0;
}
static int h_preview(int argc, unsigned char **argv){
preview = atoi((char *)argv[1]);
return 0;
}
static struct commands file_commandhandlers[] = {
{file_commandhandlers + 1, "file_cachedir", h_cachedir, 2, 2},
{NULL, "file_preview", h_preview, 2, 2},
};
static int file_loaded=0;
PLUGINAPI int PLUGINCALL file_plugin (struct pluginlink * pluginlink,
int argc, char** argv){
if(!file_loaded){
_3proxy_mutex_init(&file_mutex);
file_loaded = 1;
pl = pluginlink;
memcpy(&sso, pl->so, sizeof(struct sockfuncs));
pl->so->_prepoll = fp_prepoll;
pl->so->_send = fp_send;
pl->so->_sendto = fp_sendto;
pl->so->_recv = fp_recv;
pl->so->_recvfrom = fp_recvfrom;
pl->so->_shutdown = fp_shutdown;
pl->so->_closesocket = fp_closesocket;
fp_filter.next = pl->conf->filters;
pl->conf->filters = &fp_filter;
fp_symbols[1].next = pl->symbols.next;
pl->symbols.next = fp_symbols;
file_commandhandlers[1].next = pl->commandhandlers->next;
pl->commandhandlers->next = file_commandhandlers;
}
h_cachedir(0, NULL);
preview = 32768;
return 0;
}
#ifdef __cplusplus
}
#endif