mirror of
https://github.com/transmission/transmission.git
synced 2025-12-24 04:18:39 +00:00
rpc-server cleanups. add true wildmat control. break the Mac build a little harder.
This commit is contained in:
@@ -1195,7 +1195,7 @@ prefschanged( TrCore * core UNUSED,
|
||||
else if( !strcmp( key, PREF_KEY_RPC_ACL ) )
|
||||
{
|
||||
const char * s = pref_string_get( key );
|
||||
tr_sessionSetRPCACL( tr, s, NULL );
|
||||
tr_sessionSetRPCACL( tr, s );
|
||||
}
|
||||
else if( !strcmp( key, PREF_KEY_RPC_USERNAME ) )
|
||||
{
|
||||
|
||||
@@ -683,10 +683,8 @@ onAddressEdited( GtkCellRendererText * r UNUSED,
|
||||
GtkTreePath * path = gtk_tree_path_new_from_string( path_string );
|
||||
|
||||
acl = g_strdup_printf( "+%s", new_text );
|
||||
if( !tr_sessionTestRPCACL( session, acl, NULL ) )
|
||||
if( gtk_tree_model_get_iter( model, &iter, path ) )
|
||||
gtk_list_store_set( page->store, &iter, COL_ADDRESS, new_text,
|
||||
-1 );
|
||||
if( gtk_tree_model_get_iter( model, &iter, path ) )
|
||||
gtk_list_store_set( page->store, &iter, COL_ADDRESS, new_text, -1 );
|
||||
|
||||
g_free( acl );
|
||||
gtk_tree_path_free( path );
|
||||
|
||||
@@ -48,7 +48,8 @@ libtransmission_a_SOURCES = \
|
||||
utils.c \
|
||||
verify.c \
|
||||
web.c \
|
||||
webseed.c
|
||||
webseed.c \
|
||||
wildmat.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
bencode.h \
|
||||
|
||||
@@ -11,17 +11,13 @@
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h> /* isdigit */
|
||||
#include <errno.h>
|
||||
#include <stdlib.h> /* strtol */
|
||||
#include <string.h>
|
||||
#include <string.h> /* memcpy */
|
||||
#include <limits.h> /* INT_MAX */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h> /* open */
|
||||
|
||||
#include <unistd.h> /* unlink */
|
||||
#include <unistd.h> /* close */
|
||||
|
||||
#include <libevent/event.h>
|
||||
#include <libevent/evhttp.h>
|
||||
@@ -33,19 +29,17 @@
|
||||
#include "rpcimpl.h"
|
||||
#include "rpc-server.h"
|
||||
#include "utils.h"
|
||||
#include "web.h"
|
||||
|
||||
#define MY_NAME "RPC Server"
|
||||
#define MY_REALM "Transmission"
|
||||
|
||||
#define ACTIVE_INTERVAL_MSEC 40
|
||||
#define INACTIVE_INTERVAL_MSEC 200
|
||||
|
||||
#define MAX_TOKEN_LEN 16
|
||||
#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
|
||||
|
||||
struct acl_addr
|
||||
{
|
||||
char flag;
|
||||
char parts[4][20];
|
||||
char flag; /* '+' to allow, '-' to deny */
|
||||
char quads[4][MAX_TOKEN_LEN];
|
||||
};
|
||||
|
||||
struct tr_rpc_server
|
||||
@@ -58,8 +52,7 @@ struct tr_rpc_server
|
||||
tr_handle * session;
|
||||
char * username;
|
||||
char * password;
|
||||
char * acl_str;
|
||||
tr_list * acl_list; /* struct acl_addr */
|
||||
char * acl;
|
||||
};
|
||||
|
||||
#define dbgmsg( fmt... ) tr_deepLog( __FILE__, __LINE__, MY_NAME, ## fmt )
|
||||
@@ -97,103 +90,77 @@ parseAddress( const char * addr, struct acl_addr * setme, const char ** end )
|
||||
if( !addr ) return 0;
|
||||
|
||||
if(!((pch = strchr( addr, '.' )))) return 0;
|
||||
if( pch-addr > 20 ) return 0;
|
||||
memcpy( setme->parts[0], addr, pch-addr );
|
||||
if( pch-addr > MAX_TOKEN_LEN ) return 0;
|
||||
memcpy( setme->quads[0], addr, pch-addr );
|
||||
addr = pch + 1;
|
||||
|
||||
if(!((pch = strchr( addr, '.' )))) return 0;
|
||||
if( pch-addr > 20 ) return 0;
|
||||
memcpy( setme->parts[1], addr, pch-addr );
|
||||
if( pch-addr > MAX_TOKEN_LEN ) return 0;
|
||||
memcpy( setme->quads[1], addr, pch-addr );
|
||||
addr = pch + 1;
|
||||
|
||||
if(!((pch = strchr( addr, '.' )))) return 0;
|
||||
if( pch-addr > 20 ) return 0;
|
||||
memcpy( setme->parts[2], addr, pch-addr );
|
||||
if( pch-addr > MAX_TOKEN_LEN ) return 0;
|
||||
memcpy( setme->quads[2], addr, pch-addr );
|
||||
addr = pch + 1;
|
||||
|
||||
while( *pch && *pch!=',' ) ++pch;
|
||||
if( pch-addr > 20 ) return 0;
|
||||
memcpy( setme->parts[3], addr, pch-addr );
|
||||
if( pch-addr > MAX_TOKEN_LEN ) return 0;
|
||||
memcpy( setme->quads[3], addr, pch-addr );
|
||||
|
||||
*end = pch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
testAddress( const struct acl_addr * ref,
|
||||
const struct acl_addr * testme )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i=0; i<4; ++i )
|
||||
{
|
||||
const char * a = ref->parts[i];
|
||||
const char * b = testme->parts[i];
|
||||
if( strcmp( a, "*" ) && strcmp( a, b ) ) return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
isAddressAllowed( const tr_rpc_server * server,
|
||||
const char * address )
|
||||
{
|
||||
const char * acl;
|
||||
struct acl_addr tmp;
|
||||
const char * end;
|
||||
const int parsed = parseAddress( address, &tmp, &end );
|
||||
tr_list * l;
|
||||
|
||||
if( !parsed )
|
||||
return 0;
|
||||
|
||||
for( l=server->acl_list; l; l=l->next ) {
|
||||
const struct acl_addr * a = l->data;
|
||||
if( testAddress( a, &tmp ) ) {
|
||||
return a->flag == '+';
|
||||
}
|
||||
for( acl=server->acl; acl && *acl; )
|
||||
{
|
||||
const char * delimiter = strchr( acl, ',' );
|
||||
const int len = delimiter ? delimiter-acl : (int)strlen( acl );
|
||||
char * token = tr_strndup( acl, len );
|
||||
const int match = tr_wildmat( address, token+1 );
|
||||
tr_free( token );
|
||||
if( match )
|
||||
return *acl == '+';
|
||||
if( !delimiter )
|
||||
break;
|
||||
acl = delimiter + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static tr_list*
|
||||
parseACL( const char * acl, int * err )
|
||||
{
|
||||
tr_list * list = NULL;
|
||||
|
||||
*err = 0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
const char flag = *acl++;
|
||||
struct acl_addr tmp;
|
||||
|
||||
if( ( flag!='+' && flag!='-' ) || !parseAddress( acl, &tmp, &acl ) )
|
||||
{
|
||||
*err = 1;
|
||||
tr_list_free( &list, tr_free );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp.flag = flag;
|
||||
tr_list_append( &list, tr_memdup( &tmp, sizeof( struct acl_addr ) ) );
|
||||
if( !*acl )
|
||||
return list;
|
||||
++acl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
***
|
||||
**/
|
||||
|
||||
static void
|
||||
send_simple_response( struct evhttp_request * req, int code, const char * text )
|
||||
{
|
||||
const char * code_text = tr_webGetResponseStr( code );
|
||||
struct evbuffer * body = evbuffer_new( );
|
||||
evbuffer_add_printf( body, "<h1>%s</h1>", text ? text : code_text );
|
||||
evhttp_send_reply( req, code, code_text, body );
|
||||
evbuffer_free( body );
|
||||
}
|
||||
|
||||
static void
|
||||
handle_upload( struct evhttp_request * req, struct tr_rpc_server * server )
|
||||
{
|
||||
if( req->type != EVHTTP_REQ_POST )
|
||||
{
|
||||
evhttp_send_reply( req, HTTP_BADREQUEST, "Bad Request", NULL );
|
||||
send_simple_response( req, HTTP_BADREQUEST, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -213,7 +180,7 @@ handle_upload( struct evhttp_request * req, struct tr_rpc_server * server )
|
||||
const size_t boundary_len = strlen( boundary );
|
||||
|
||||
const char * delim = tr_memmem( in, inlen, boundary, boundary_len );
|
||||
if( delim ) do
|
||||
while( delim )
|
||||
{
|
||||
size_t part_len;
|
||||
const char * part = delim + boundary_len;
|
||||
@@ -263,14 +230,13 @@ handle_upload( struct evhttp_request * req, struct tr_rpc_server * server )
|
||||
tr_free( text );
|
||||
}
|
||||
}
|
||||
while( delim );
|
||||
|
||||
tr_free( boundary );
|
||||
|
||||
/* use xml here because json responses to file uploads is trouble.
|
||||
* see http://www.malsup.com/jquery/form/#sample7 for details */
|
||||
evhttp_add_header(req->output_headers, "Content-Type", "text/xml; charset=UTF-8" );
|
||||
evhttp_send_reply( req, HTTP_OK, "Success", NULL );
|
||||
send_simple_response( req, HTTP_OK, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,12 +271,16 @@ serve_file( struct evhttp_request * req, const char * path )
|
||||
if( req->type != EVHTTP_REQ_GET )
|
||||
{
|
||||
evhttp_add_header(req->output_headers, "Allow", "GET");
|
||||
evhttp_send_reply(req, 405, "Method Not Allowed", NULL);
|
||||
send_simple_response( req, 405, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
const int fd = open( path, O_RDONLY, 0 );
|
||||
if( fd != -1 )
|
||||
if( fd == -1 )
|
||||
{
|
||||
send_simple_response( req, HTTP_NOTFOUND, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
char size[12];
|
||||
struct evbuffer * buf = evbuffer_new();
|
||||
@@ -319,18 +289,11 @@ serve_file( struct evhttp_request * req, const char * path )
|
||||
evhttp_add_header(req->output_headers, "Content-Type", mimetype_guess( path ) );
|
||||
snprintf(size, sizeof(size), "%zu", EVBUFFER_LENGTH( buf ) );
|
||||
evhttp_add_header(req->output_headers, "Content-Length", size );
|
||||
evhttp_send_reply(req, HTTP_OK, "OK", buf);
|
||||
evhttp_send_reply( req, HTTP_OK, "OK", buf );
|
||||
|
||||
evbuffer_free(buf);
|
||||
close( fd );
|
||||
}
|
||||
else
|
||||
{
|
||||
struct evbuffer * buf = evbuffer_new();
|
||||
evbuffer_add_printf(buf, "<h1>Not Found</h1>");
|
||||
evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
|
||||
evbuffer_free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +307,7 @@ handle_clutch( struct evhttp_request * req, struct tr_rpc_server * server )
|
||||
|
||||
evbuffer_add_printf( buf, "%s%s", tr_getClutchDir( server->session ), TR_PATH_DELIMITER_STR );
|
||||
uri = req->uri + 18;
|
||||
if( !*uri || (*uri=='?') )
|
||||
if( (*uri=='?') || (*uri=='\0') )
|
||||
evbuffer_add_printf( buf, "index.html" );
|
||||
else {
|
||||
const char * pch;
|
||||
@@ -404,7 +367,7 @@ handle_request( struct evhttp_request * req, void * arg )
|
||||
char * user = NULL;
|
||||
char * pass = NULL;
|
||||
|
||||
evhttp_add_header( req->output_headers, "Server", "Transmission" );
|
||||
evhttp_add_header( req->output_headers, "Server", MY_REALM );
|
||||
|
||||
auth = evhttp_find_header( req->input_headers, "Authorization" );
|
||||
|
||||
@@ -418,29 +381,25 @@ handle_request( struct evhttp_request * req, void * arg )
|
||||
}
|
||||
}
|
||||
|
||||
if( server->acl_list && !isAddressAllowed( server, req->remote_host ) )
|
||||
if( server->acl && !isAddressAllowed( server, req->remote_host ) )
|
||||
{
|
||||
struct evbuffer * buf = evbuffer_new();
|
||||
evbuffer_add_printf(buf, "<h1>Unauthorized IP Address</h1>");
|
||||
evhttp_send_reply(req, 401, "Unauthorized", buf );
|
||||
send_simple_response( req, 401, "Unauthorized IP Address" );
|
||||
}
|
||||
else if( server->isPasswordEnabled && ( !pass
|
||||
|| !user
|
||||
|| strcmp( server->username, user )
|
||||
|| strcmp( server->password, pass ) ) )
|
||||
{
|
||||
struct evbuffer * buf = evbuffer_new();
|
||||
evhttp_add_header( req->output_headers,
|
||||
"WWW-Authenticate", "Basic realm=\"Transmission\"");
|
||||
evbuffer_add_printf(buf, "<h1>Unauthorized User</h1>");
|
||||
evhttp_send_reply(req, 401, "Unauthorized", buf);
|
||||
"WWW-Authenticate", "Basic realm=\"" MY_REALM "\"" );
|
||||
send_simple_response( req, 401, "Unauthorized User" );
|
||||
}
|
||||
else if( !strcmp( req->uri, "/transmission/web" ) ||
|
||||
!strcmp( req->uri, "/transmission/clutch" ) ||
|
||||
!strcmp( req->uri, "/" ) )
|
||||
{
|
||||
evhttp_add_header( req->output_headers, "Location", "/transmission/web/" );
|
||||
evhttp_send_reply(req, HTTP_MOVEPERM, "Moved Permanently", NULL );
|
||||
send_simple_response( req, HTTP_MOVEPERM, NULL );
|
||||
}
|
||||
else if( !strncmp( req->uri, "/transmission/web/", 18 ) )
|
||||
{
|
||||
@@ -456,9 +415,7 @@ handle_request( struct evhttp_request * req, void * arg )
|
||||
}
|
||||
else
|
||||
{
|
||||
struct evbuffer *buf = evbuffer_new( );
|
||||
evbuffer_add_printf(buf, "<h1>Not Found</h1>");
|
||||
evhttp_send_reply(req, HTTP_NOTFOUND, "Not Found", buf);
|
||||
send_simple_response( req, HTTP_NOTFOUND, NULL );
|
||||
}
|
||||
|
||||
tr_free( user );
|
||||
@@ -500,7 +457,7 @@ tr_rpcSetEnabled( tr_rpc_server * server,
|
||||
int
|
||||
tr_rpcIsEnabled( const tr_rpc_server * server )
|
||||
{
|
||||
return server->httpd != NULL;
|
||||
return server->isEnabled;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -525,46 +482,18 @@ tr_rpcGetPort( const tr_rpc_server * server )
|
||||
return server->port;
|
||||
}
|
||||
|
||||
int
|
||||
tr_rpcTestACL( const tr_rpc_server * server UNUSED,
|
||||
const char * acl,
|
||||
char ** setme_errmsg )
|
||||
{
|
||||
int err = 0;
|
||||
tr_list * list = parseACL( acl, &err );
|
||||
if( err )
|
||||
*setme_errmsg = tr_strdup( "invalid ACL" );
|
||||
tr_list_free( &list, tr_free );
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
tr_rpcSetACL( tr_rpc_server * server,
|
||||
const char * acl_str,
|
||||
char ** setme_errmsg )
|
||||
const char * acl )
|
||||
{
|
||||
int err = 0;
|
||||
tr_list * list = parseACL( acl_str, &err );
|
||||
|
||||
if( err )
|
||||
{
|
||||
*setme_errmsg = tr_strdup( "invalid ACL" );
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_free( server->acl_str );
|
||||
tr_list_free( &server->acl_list, tr_free );
|
||||
server->acl_str = tr_strdup( acl_str );
|
||||
server->acl_list = list;
|
||||
}
|
||||
|
||||
return err;
|
||||
tr_free( server->acl );
|
||||
server->acl = tr_strdup( acl );
|
||||
}
|
||||
|
||||
char*
|
||||
tr_rpcGetACL( const tr_rpc_server * server )
|
||||
{
|
||||
return tr_strdup( server->acl_str ? server->acl_str : "" );
|
||||
return tr_strdup( server->acl ? server->acl : "" );
|
||||
}
|
||||
|
||||
/****
|
||||
@@ -605,7 +534,7 @@ void
|
||||
tr_rpcSetPasswordEnabled( tr_rpc_server * server,
|
||||
int isEnabled )
|
||||
{
|
||||
server->isPasswordEnabled = isEnabled;
|
||||
server->isPasswordEnabled = isEnabled != 0;
|
||||
dbgmsg( "setting 'password enabled' to %d", isEnabled );
|
||||
}
|
||||
|
||||
@@ -627,8 +556,7 @@ tr_rpcClose( tr_rpc_server ** ps )
|
||||
*ps = NULL;
|
||||
|
||||
stopServer( s );
|
||||
tr_list_free( &s->acl_list, tr_free );
|
||||
tr_free( s->acl_str );
|
||||
tr_free( s->acl );
|
||||
tr_free( s->username );
|
||||
tr_free( s->password );
|
||||
tr_free( s );
|
||||
@@ -638,26 +566,17 @@ tr_rpc_server *
|
||||
tr_rpcInit( tr_handle * session,
|
||||
int isEnabled,
|
||||
int port,
|
||||
const char * acl_str,
|
||||
const char * acl,
|
||||
int isPasswordEnabled,
|
||||
const char * username,
|
||||
const char * password )
|
||||
{
|
||||
tr_rpc_server * s;
|
||||
int err = 0;
|
||||
tr_list * list = parseACL( acl_str, &err );
|
||||
|
||||
if( err ) {
|
||||
acl_str = TR_DEFAULT_RPC_ACL;
|
||||
tr_nerr( MY_NAME, "using fallback ACL \"%s\"", acl_str );
|
||||
list = parseACL( acl_str, &err );
|
||||
}
|
||||
|
||||
s = tr_new0( tr_rpc_server, 1 );
|
||||
s->session = session;
|
||||
s->port = port;
|
||||
s->acl_str = tr_strdup( acl_str );
|
||||
s->acl_list = list;
|
||||
s->acl = tr_strdup( acl && *acl ? acl : TR_DEFAULT_RPC_ACL );
|
||||
s->username = tr_strdup( username );
|
||||
s->password = tr_strdup( password );
|
||||
s->isPasswordEnabled = isPasswordEnabled != 0;
|
||||
|
||||
@@ -39,13 +39,8 @@ int tr_rpcSetTest( const tr_rpc_server * server,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg );
|
||||
|
||||
int tr_rpcTestACL( const tr_rpc_server * server,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg );
|
||||
|
||||
int tr_rpcSetACL( tr_rpc_server * server,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg );
|
||||
void tr_rpcSetACL( tr_rpc_server * server,
|
||||
const char * acl );
|
||||
|
||||
char* tr_rpcGetACL( const tr_rpc_server * server );
|
||||
|
||||
|
||||
@@ -1002,20 +1002,11 @@ tr_sessionSetRPCCallback( tr_session * session,
|
||||
session->rpc_func_user_data = user_data;
|
||||
}
|
||||
|
||||
int
|
||||
tr_sessionTestRPCACL( const tr_session * session,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg )
|
||||
{
|
||||
return tr_rpcTestACL( session->rpcServer, acl, allocme_errmsg );
|
||||
}
|
||||
|
||||
int
|
||||
void
|
||||
tr_sessionSetRPCACL( tr_session * session,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg )
|
||||
const char * acl )
|
||||
{
|
||||
return tr_rpcSetACL( session->rpcServer, acl, allocme_errmsg );
|
||||
return tr_rpcSetACL( session->rpcServer, acl );
|
||||
}
|
||||
|
||||
char*
|
||||
|
||||
@@ -345,52 +345,16 @@ void tr_sessionSetRPCPort( tr_handle *,
|
||||
@see tr_sessionSetRPCPort */
|
||||
int tr_sessionGetRPCPort( const tr_handle * );
|
||||
|
||||
/**
|
||||
* @brief test an ACL's syntax without modifying the RPC settings.
|
||||
*
|
||||
* ACL is a comma separated list of IP subnets, each subnet is prepended
|
||||
* by a '+' or '-' sign to denote 'allow' or 'deny'. If the subnet mask
|
||||
* is omitted, like "-1.2.3.4", it means a single IP address. The mask
|
||||
* may vary from 0 to 32 inclusive. A simple primer on x.x.x.x/n notation
|
||||
* can be found at <http://25yearsofprogramming.com/blog/20070803.htm>.
|
||||
*
|
||||
* Since wildcards are more familiar to most users than netmasks,
|
||||
* libtransmission supports a wildcard notation that it
|
||||
* converts into cidr required by the embedded http server.
|
||||
* So, notation like "+192.168.*.*" is accepted by libtransmission and is
|
||||
* converted to "+192.168.0.0/16" before it reaches the server.
|
||||
|
||||
* @param acl the ACL to test
|
||||
* @param allocme_errmsg If the ACL can't be parsed, this is set to a
|
||||
* newly-allocated error string describing the problem.
|
||||
* The client should tr_free() this string when done.
|
||||
* @return 0 on success, -1 on failure due to an unparseable ACL.
|
||||
*/
|
||||
int tr_sessionTestRPCACL( const tr_handle * session,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg );
|
||||
|
||||
/**
|
||||
* @brief Specify access control list (ACL).
|
||||
ACL is a comma separated list
|
||||
* of IP subnets, each subnet is prepended by a '-' or '+' sign.
|
||||
* Plus means allow, minus means deny. If the subnet mask is omitted,
|
||||
* like "-1.2.3.4", it means a single IP address. The mask may vary
|
||||
* from 0 to 32 inclusive.
|
||||
*
|
||||
* http://25yearsofprogramming.com/blog/20070803.htm has a simple
|
||||
* primer on x.x.x.x/n notation for those interested.
|
||||
*
|
||||
* The parameters and return value follow the same behavior as
|
||||
* tr_sessionTestRPCACL().
|
||||
*
|
||||
* @see tr_sessionTestRPCACL
|
||||
* @see tr_sessionInitFull
|
||||
* @see tr_sessionGetRPCACL
|
||||
* ACL is a comma-delimited list of dotted-quad IP addresses, each preceded
|
||||
* by a '+' or '-' sign to denote 'allow' or 'deny'. Wildmat notation is
|
||||
* supported, meaning that '?' is interpreted as a single-character wildcard
|
||||
* and '*' is interprted as a multi-character wildcard.
|
||||
*/
|
||||
int tr_sessionSetRPCACL( tr_session * session,
|
||||
const char * acl,
|
||||
char ** allocme_errmsg );
|
||||
void tr_sessionSetRPCACL( tr_session * session,
|
||||
const char * acl );
|
||||
|
||||
/** @brief get the Access Control List for allowing/denying RPC requests.
|
||||
@return a comma-separated string of ACL rules. tr_free() when done.
|
||||
|
||||
@@ -123,6 +123,9 @@ const char* tr_strip_positional_args( const char* fmt );
|
||||
#define tr_inf( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_INF, NULL, ## a )
|
||||
#define tr_dbg( a... ) tr_msg( __FILE__, __LINE__, TR_MSG_DBG, NULL, ## a )
|
||||
|
||||
int tr_wildmat( const char * text,
|
||||
const char * pattern );
|
||||
|
||||
void tr_msgInit( void );
|
||||
|
||||
void tr_msg( const char * file,
|
||||
|
||||
124
libtransmission/wildmat.c
Normal file
124
libtransmission/wildmat.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/* $XConsortium: wildmat.c,v 1.2 94/04/13 18:40:59 rws Exp $ */
|
||||
/*
|
||||
**
|
||||
** Do shell-style pattern matching for ?, \, [], and * characters.
|
||||
** Might not be robust in face of malformed patterns; e.g., "foo[a-"
|
||||
** could cause a segmentation violation. It is 8bit clean.
|
||||
**
|
||||
** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
|
||||
** Rich $alz is now <rsalz@bbn.com>.
|
||||
** April, 1991: Replaced mutually-recursive calls with in-line code
|
||||
** for the star character.
|
||||
**
|
||||
** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
|
||||
** This can greatly speed up failing wildcard patterns. For example:
|
||||
** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
|
||||
** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
|
||||
** text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
|
||||
** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without
|
||||
** the ABORT, then it takes 22310 calls to fail. Ugh. The following
|
||||
** explanation is from Lars:
|
||||
** The precondition that must be fulfilled is that DoMatch will consume
|
||||
** at least one character in text. This is true if *p is neither '*' nor
|
||||
** '\0'.) The last return has ABORT instead of FALSE to avoid quadratic
|
||||
** behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With
|
||||
** FALSE, each star-loop has to run to the end of the text; with ABORT
|
||||
** only the last one does.
|
||||
**
|
||||
** Once the control of one instance of DoMatch enters the star-loop, that
|
||||
** instance will return either TRUE or ABORT, and any calling instance
|
||||
** will therefore return immediately after (without calling recursively
|
||||
** again). In effect, only one star-loop is ever active. It would be
|
||||
** possible to modify the code to maintain this context explicitly,
|
||||
** eliminating all recursive calls at the cost of some complication and
|
||||
** loss of clarity (and the ABORT stuff seems to be unclear enough by
|
||||
** itself). I think it would be unwise to try to get this into a
|
||||
** released version unless you have a good test data base to try it out
|
||||
** on.
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define ABORT -1
|
||||
|
||||
|
||||
/* What character marks an inverted character class? */
|
||||
#define NEGATE_CLASS '^'
|
||||
/* Is "*" a common pattern? */
|
||||
#define OPTIMIZE_JUST_STAR
|
||||
/* Do tar(1) matching rules, which ignore a trailing slash? */
|
||||
#undef MATCH_TAR_PATTERN
|
||||
|
||||
|
||||
/*
|
||||
** Match text and p, return TRUE, FALSE, or ABORT.
|
||||
*/
|
||||
static int
|
||||
DoMatch( const char * text, const char * p )
|
||||
{
|
||||
register int last;
|
||||
register int matched;
|
||||
register int reverse;
|
||||
|
||||
for ( ; *p; text++, p++) {
|
||||
if (*text == '\0' && *p != '*')
|
||||
return ABORT;
|
||||
switch (*p) {
|
||||
case '\\':
|
||||
/* Literal match with following character. */
|
||||
p++;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
if (*text != *p)
|
||||
return FALSE;
|
||||
continue;
|
||||
case '?':
|
||||
/* Match anything. */
|
||||
continue;
|
||||
case '*':
|
||||
while (*++p == '*')
|
||||
/* Consecutive stars act just like one. */
|
||||
continue;
|
||||
if (*p == '\0')
|
||||
/* Trailing star matches everything. */
|
||||
return TRUE;
|
||||
while (*text)
|
||||
if ((matched = DoMatch(text++, p)) != FALSE)
|
||||
return matched;
|
||||
return ABORT;
|
||||
case '[':
|
||||
reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
|
||||
if (reverse)
|
||||
/* Inverted character class. */
|
||||
p++;
|
||||
for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p)
|
||||
/* This next line requires a good C compiler. */
|
||||
if (*p == '-' ? *text <= *++p && *text >= last : *text == *p)
|
||||
matched = TRUE;
|
||||
if (matched == reverse)
|
||||
return FALSE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MATCH_TAR_PATTERN
|
||||
if (*text == '/')
|
||||
return TRUE;
|
||||
#endif /* MATCH_TAR_ATTERN */
|
||||
return *text == '\0';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** User-level routine. Returns TRUE or FALSE.
|
||||
*/
|
||||
int
|
||||
tr_wildmat(const char * text, const char * p )
|
||||
{
|
||||
if (p[0] == '*' && p[1] == '\0')
|
||||
return TRUE;
|
||||
|
||||
return DoMatch(text, p) == TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user