pjs/lib/libpics/picsapi.c

665 строки
14 KiB
C

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* Glue code to make PICS work in the client. This is just a thin
* layer between the W3C code and our layout engine to digest PICS labels.
* Coded by Lou Montulli
*/
#include "xp.h"
#include "cslutils.h"
#include "csll.h"
#include "csllst.h"
#include "pics.h"
#include "prefapi.h"
#include "xpgetstr.h"
#include "sechash.h"
#include "base64.h"
extern int XP_ALERT_PROMPT_JAVA_CAPIBILITIES_PASSWORD;
extern int XP_ALERT_PROMPT_JAVA_CAPIBILITIES_PASSWORD_FAILED_ONCE;
typedef struct {
PICS_RatingsStruct * rs;
XP_Bool rs_invalid;
} ClosureData;
PRIVATE StateRet_t
target_callback(CSLabel_t *pCSLabel,
CSParse_t * pCSParse,
CSLLTC_t target, XP_Bool closed,
void * pClosure)
{
char * ratingname;
char * ratingstr;
ClosureData *cd = (ClosureData *)pClosure;
/* closed signifies that the parsing is done for that label */
if(!cd || !closed)
return StateRet_OK;
if(target == CSLLTC_SINGLE)
{
LabelOptions_t * lo = CSLabel_getLabelOptions(pCSLabel);
if(lo)
{
if(lo->generic.state)
{
cd->rs->generic = TRUE;
}
if(lo->fur.value && !cd->rs->fur)
{
StrAllocCopy(cd->rs->fur, lo->fur.value);
}
}
}
else if(target == CSLLTC_RATING)
{
PICS_RatingsStruct *rating_struct = cd->rs;
LabelOptions_t * lo = CSLabel_getLabelOptions(pCSLabel);
ratingstr = CSLabel_getRatingStr(pCSLabel);
ratingname = CSLabel_getRatingName(pCSLabel);
if(ratingname)
{
LabelRating_t * label_rating;
ServiceInfo_t * service_info;
service_info = CSLabel_getServiceInfo(pCSLabel);
if(service_info && !rating_struct->service)
{
rating_struct->service = XP_STRDUP(service_info->rating_service.value);
}
label_rating = CSLabel_getLabelRating(pCSLabel);
if(label_rating)
{
double value;
PICS_RatingValue *rating_value = XP_NEW_ZAP(PICS_RatingValue);
value = label_rating->value.value;
if(rating_value)
{
rating_value->value = label_rating->value.value;
rating_value->name = XP_STRDUP(label_rating->identifier.value);
if(rating_value->name)
{
/* insert it into the list */
XP_ListAddObject(rating_struct->ratings, rating_value);
}
else
{
/* error, cleanup */
XP_FREE(rating_value);
}
}
}
}
}
return StateRet_OK;
}
PRIVATE StateRet_t
parse_error_handler(CSLabel_t * pCSLabel, CSParse_t * pCSParse,
const char * token, char demark,
StateRet_t errorCode)
{
return errorCode;
}
/* return NULL or ratings struct */
PUBLIC PICS_RatingsStruct *
PICS_ParsePICSLable(char * label)
{
CSParse_t *CSParse_handle;
ClosureData *cd;
PICS_RatingsStruct *rs;
CSDoMore_t status;
if(!label)
return NULL;
cd = XP_NEW_ZAP(ClosureData);
if(!cd)
return NULL;
rs = XP_NEW_ZAP(PICS_RatingsStruct);
if(!rs)
{
XP_FREE(cd);
return NULL;
}
rs->ratings = XP_ListNew();
cd->rs = rs;
/* parse pics label using w3c api */
CSParse_handle = CSParse_newLabel(&target_callback, &parse_error_handler);
if(!CSParse_handle)
return NULL;
do {
status = CSParse_parseChunk(CSParse_handle, label, XP_STRLEN(label), cd);
} while(status == CSDoMore_more);
if(cd->rs_invalid)
{
PICS_FreeRatingsStruct(rs);
rs = NULL;
}
XP_FREE(cd);
CSParse_deleteLabel(CSParse_handle);
return(rs);
}
PUBLIC void
PICS_FreeRatingsStruct(PICS_RatingsStruct *rs)
{
if(rs)
{
PICS_RatingValue *rv;
while((rv = XP_ListRemoveTopObject(rs->ratings)) != NULL)
{
XP_FREE(rv->name);
XP_FREE(rv);
}
XP_FREE(rs);
}
}
#define PICS_DOMAIN "browser.PICS."
#define PICS_ENABLED_PREF PICS_DOMAIN"ratings_enabled"
#define PICS_MUST_BE_RATED_PREF PICS_DOMAIN"pages_must_be_rated"
#define PICS_DISABLED_FOR_SESSION PICS_DOMAIN"disable_for_this_session"
#define PICS_REENABLE_FOR_SESSION PICS_DOMAIN"reenable_for_this_session"
#define JAVA_SECURITY_PASSWORD "signed.applets.capabilitiesDB.password"
Bool pics_ratings_enabled = FALSE;
Bool pics_pages_must_be_rated_pref = FALSE;
Bool pics_disabled_for_this_session = FALSE;
int pics_violence_pref = 0;
int pics_sexual_pref = 0;
int pics_language_pref = 0;
int pics_nudity_pref = 0;
/* if TRUE the user can allow additional java and JS
* capibilities. UniversalFileWrite, etc.
*/
Bool pics_java_capabilities_enabled = TRUE;
int PR_CALLBACK
pics_pref_change(const char *pref_name, void *closure)
{
XP_Bool bool_rv;
if(!PREF_GetBoolPref(PICS_ENABLED_PREF, &bool_rv))
pics_ratings_enabled = bool_rv;
if(!PREF_GetBoolPref(PICS_MUST_BE_RATED_PREF, &bool_rv))
pics_pages_must_be_rated_pref = bool_rv;
if(!PREF_GetBoolPref(PICS_DISABLED_FOR_SESSION, &bool_rv))
{
if(bool_rv)
{
pics_disabled_for_this_session = TRUE;
PREF_SetBoolPref(PICS_DISABLED_FOR_SESSION, FALSE);
}
}
if(!PREF_GetBoolPref(PICS_REENABLE_FOR_SESSION, &bool_rv))
{
if(bool_rv)
{
pics_disabled_for_this_session = FALSE;
PREF_SetBoolPref(PICS_REENABLE_FOR_SESSION, FALSE);
}
}
return 0;
}
PRIVATE char *
pics_hash_password(char *pw)
{
SECStatus status;
unsigned char result[SHA1_LENGTH];
status = SHA1_HashBuf(result, (unsigned char *)pw, XP_STRLEN(pw));
if (status != SECSuccess)
return NULL;
return(BTOA_DataToAscii(result, SHA1_LENGTH));
}
PUBLIC void
PICS_Init(MWContext *context)
{
static XP_Bool first_time=TRUE;
if(!first_time)
{
return;
}
else
{
char *password=NULL;
first_time = FALSE;
/* get the prefs */
pics_pref_change(PICS_DOMAIN, NULL);
PREF_RegisterCallback(PICS_DOMAIN, pics_pref_change, NULL);
/* check for security pref that password disables the enableing of
* java permissions
*/
if(PREF_CopyCharPref(JAVA_SECURITY_PASSWORD, &password))
password = NULL;
if(password && *password)
{
/* get prompt string from registry
*/
char *prompt_string = XP_GetString(XP_ALERT_PROMPT_JAVA_CAPIBILITIES_PASSWORD);
char *user_password;
char *hashed_password;
prompt_again:
/* prompt the user for the password
*/
user_password = FE_PromptPassword(context, prompt_string);
/* ### one-way hash password */
if(user_password)
{
hashed_password = pics_hash_password(user_password);
}
else
{
hashed_password = NULL;
}
if(!hashed_password)
{
pics_java_capabilities_enabled = FALSE;
}
else if(!XP_STRCMP(hashed_password, password))
{
pics_java_capabilities_enabled = TRUE;
}
else
{
XP_FREE(user_password);
XP_FREE(hashed_password);
prompt_string = XP_GetString(XP_ALERT_PROMPT_JAVA_CAPIBILITIES_PASSWORD_FAILED_ONCE);
goto prompt_again;
}
XP_FREEIF(user_password);
XP_FREEIF(hashed_password);
}
XP_FREEIF(password);
}
}
PUBLIC XP_Bool
PICS_CanUserEnableAdditionalJavaCapabilities(void)
{
return(pics_java_capabilities_enabled);
}
PUBLIC XP_Bool
PICS_IsPICSEnabledByUser(void)
{
/* short circuit */
if(pics_disabled_for_this_session)
return FALSE;
return(pics_ratings_enabled);
}
PUBLIC XP_Bool
PICS_AreRatingsRequired(void)
{
return pics_pages_must_be_rated_pref;
}
PRIVATE char *
illegal_to_underscore(char *string)
{
char* ptr = string;
if(!string)
return NULL;
if(!XP_IS_ALPHA(*ptr))
*ptr = '_';
for(ptr++; *ptr; ptr++)
if(!XP_IS_ALPHA(*ptr) && !XP_IS_DIGIT(*ptr))
*ptr = '_';
return string;
}
PRIVATE char *
lowercase_string(char *string)
{
char *ptr = string;
if(!string)
return NULL;
for(; *ptr; ptr++)
*ptr = XP_TO_LOWER(*ptr);
return string;
}
#define PICS_URL_PREFIX "about:pics"
/* returns a URL string from a RatingsStruct
* that includes the service URL and rating info
*/
PUBLIC char *
PICS_RStoURL(PICS_RatingsStruct *rs, char *cur_page_url)
{
char *rv;
char *escaped_cur_page=NULL;
if(cur_page_url)
{
escaped_cur_page = NET_Escape(cur_page_url, URL_PATH);
if(!escaped_cur_page)
return NULL;
}
rv = PR_smprintf("%s?Destination=%s",
PICS_URL_PREFIX,
escaped_cur_page ? escaped_cur_page : "none");
XP_FREE(escaped_cur_page);
if(!rs || !rs->service)
{
StrAllocCat(rv, "&NO_RATING");
return(rv);
}
else
{
XP_List *list_ptr = rs->ratings;
PICS_RatingValue *rating_value;
char *escaped_service = NET_Escape(rs->service, URL_PATH);
if(!escaped_service)
return NULL;
StrAllocCat(rv, "&Service=");
StrAllocCat(rv, escaped_service);
XP_FREE(escaped_service);
while((rating_value = XP_ListNextObject(list_ptr)) != NULL)
{
char *add;
char *escaped_name = NET_Escape(
illegal_to_underscore(rating_value->name),
URL_PATH);
if(!escaped_name)
{
XP_FREE(rv);
return NULL;
}
add = PR_smprintf("&%s=%f", escaped_name, rating_value->value);
XP_FREE(escaped_name);
StrAllocCat(rv, add);
XP_FREE(add);
}
return rv;
}
XP_ASSERT(0); /* should never get here */
return NULL;
}
XP_List *pics_tree_ratings=NULL;
PRIVATE void
pics_add_rs_to_tree_ratings(PICS_RatingsStruct *rs)
{
char *path;
if(!pics_tree_ratings)
{
pics_tree_ratings = XP_ListNew();
if(!pics_tree_ratings)
return;
}
if(!rs->fur || !rs->generic)
return; /* doesn't belong here */
/* make sure it's not in the list already */
if(PICS_CheckForValidTreeRating(rs->fur))
return;
/* make sure the fur address smells like a URL and has
* a real host name (at least two dots)
*
* reject "http://" or "http://www"
*/
if(!NET_URL_Type(rs->fur))
return;
path = NET_ParseURL(rs->fur, GET_PATH_PART);
/* if it has a path it's ok */
if(!path || !*path)
{
/* if it doesn't have a path it needs at least two dots */
char *ptr;
char *hostname = NET_ParseURL(rs->fur, GET_HOST_PART);
if(!hostname)
return;
if(!(ptr = XP_STRCHR(hostname, '.'))
|| !XP_STRCHR(ptr+1, '.'))
{
XP_FREE(hostname);
XP_FREEIF(path);
return;
}
XP_FREE(hostname);
}
XP_FREE(path);
XP_ListAddObject(pics_tree_ratings, rs->fur);
return;
}
PUBLIC XP_Bool
PICS_CheckForValidTreeRating(char *url_address)
{
XP_List *list_ptr;
char *valid_tree;
if(!pics_tree_ratings)
return FALSE;
list_ptr = pics_tree_ratings;
while((valid_tree = XP_ListNextObject(list_ptr)))
{
if(!XP_STRNCASECMP(url_address, valid_tree, XP_STRLEN(valid_tree)))
return TRUE;
}
return FALSE;
}
/* returns TRUE if page should be censored
* FALSE if page is allowed to be shown
*/
PUBLIC PICS_PassFailReturnVal
PICS_CompareToUserSettings(PICS_RatingsStruct *rs, char *cur_page_url)
{
int32 int_pref;
XP_Bool bool_pref;
char * pref_prefix;
char * pref_string=NULL;
char * escaped_service;
PICS_PassFailReturnVal rv = PICS_RATINGS_PASSED;
XP_List *list_ptr;
PICS_RatingValue *rating_value;
if(!rs || !rs->service)
{
return PICS_NO_RATINGS;
}
#define PICS_SERVICE_DOMAIN PICS_DOMAIN"service."
#define PICS_SERVICE_ENABLED "service_enabled"
/* cycle through list of ratings and compare to the users prefs */
list_ptr = rs->ratings;
pref_prefix = XP_STRDUP(PICS_SERVICE_DOMAIN);
/* need to deal with bad characters */
escaped_service = XP_STRDUP(rs->service);
escaped_service = illegal_to_underscore(escaped_service);
escaped_service = lowercase_string(escaped_service);
if(!escaped_service)
return PICS_RATINGS_FAILED;
StrAllocCat(pref_prefix, escaped_service);
XP_FREE(escaped_service);
if(!pref_prefix)
return PICS_RATINGS_FAILED;
/* verify that this type of rating system is enabled */
pref_string = PR_smprintf("%s.%s", pref_prefix, PICS_SERVICE_ENABLED);
if(!pref_string)
goto cleanup;
if(!PREF_GetBoolPref(pref_string, &bool_pref))
{
if(!bool_pref)
{
/* this is an unenabled ratings service */
rv = PICS_NO_RATINGS;
XP_FREE(pref_string);
goto cleanup;
}
}
else
{
/* this is an unsupported ratings service */
rv = PICS_NO_RATINGS;
XP_FREE(pref_string);
goto cleanup;
}
XP_FREE(pref_string);
while((rating_value = XP_ListNextObject(list_ptr)) != NULL)
{
/* compose pref lookup string */
pref_string = PR_smprintf("%s.%s",
pref_prefix,
illegal_to_underscore(rating_value->name));
if(!pref_string)
goto cleanup;
/* find the value in the prefs if it exists
* if it does compare it to the value given and if
* less than, censer the page.
*/
if(!PREF_GetIntPref(pref_string, &int_pref))
{
if(rating_value->value > int_pref)
{
rv = PICS_RATINGS_FAILED;
XP_FREE(pref_string);
goto cleanup;
}
}
XP_FREE(pref_string);
}
cleanup:
XP_FREE(pref_prefix);
/* make sure this rating applies to this page */
if(rs->fur)
{
if(XP_STRNCASECMP(cur_page_url, rs->fur, XP_STRLEN(rs->fur)))
rv = PICS_NO_RATINGS;
}
if(rv == PICS_RATINGS_PASSED && rs->generic)
{
/* rating should apply to a whole tree, add to list */
pics_add_rs_to_tree_ratings(rs);
}
return rv;
}