/* Copyright 1997 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /***************************************************/ /* File : Authorise.c */ /* */ /* Purpose: Browser remote authorisation services */ /* (aka. authentication). */ /* */ /* Author : A.D.Hodgkinson */ /* */ /* History: 24-Apr-97: Created. */ /***************************************************/ #include <stdlib.h> #include <string.h> #include "flex.h" #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "window.h" #include "gadgets.h" #include "svcprint.h" #include "Global.h" #include "Utils.h" #include "Browser.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "Toolbars.h" #include "URLutils.h" #include "Windows.h" #include "Authorise.h" /* Locals */ static ObjectId authorise_dbox = 0; static char current_realm[Limits_Realm]; /*************************************************/ /* authorise_create_dialogue */ /* */ /* Creates an authorisation dialogue and */ /* installs relevant event handlers with a given */ /* client handle. If the dialogue already exists */ /* then old handlers are removed and new ones */ /* installed. */ /* */ /* Parameters: The client handle to pass to the */ /* event handlers, as a void *; */ /* */ /* Pointer to an ObjectId, in which */ /* the ObjectId of the dialogue will */ /* be written (may be NULL). */ /*************************************************/ _kernel_oserror * authorise_create_dialogue(void * handle, ObjectId * id) { if (authorise_dbox) { /* If there's already a dialogue, remove event handlers that */ /* are already present prior to installing new ones. This is */ /* to allow the client handle to change. */ RetError(event_deregister_toolbox_handlers_for_object(authorise_dbox)); } else { /* Otherwise, create the dialogue */ RetError(toolbox_create_object(0, "Authorise", &authorise_dbox)); /* Set a client handle of 0 for now */ RetError(toolbox_set_client_handle(0, authorise_dbox, NULL)); /* Modify writables if necessary */ { char username[Limits_AuthUserWrit]; char password[Limits_AuthPassWrit]; /* First, the user name writable */ RetError(windows_process_component_text(authorise_dbox, AuthUserWrit, username, sizeof(username), 0, 1)); /* Next, the password writable */ RetError(windows_process_component_text(authorise_dbox, AuthPassWrit, password, sizeof(password), 0, 1)); } } if (id) *id = authorise_dbox; /* Install the event handlers and exit */ RetError(event_register_toolbox_handler(authorise_dbox, Window_HasBeenHidden, authorise_cancel, handle)); RetError(event_register_toolbox_handler(authorise_dbox, EAuthAuthorise, authorise_authorise, handle)); /* Animation handler if there's an appropriate gadget */ { int temp_type; if ( controls.dbox_anims && !gadget_get_type(0, authorise_dbox, StatusBarAnimAnim, &temp_type) ) register_null_claimant(Wimp_ENull, toolbars_animate_slow, (void *) authorise_dbox); } return event_register_toolbox_handler(authorise_dbox, EAuthCancel, authorise_cancel, handle); } /*************************************************/ /* authorise_return_dialogue_id() */ /* */ /* Returns the ID of the current authorisation */ /* dialogue (will be 0 if there is no dialogue */ /* present at the moment). */ /* */ /* Returns: Object ID of the authorisation dbox. */ /*************************************************/ ObjectId authorise_return_dialogue_id(void) { return authorise_dbox; } /*************************************************/ /* authorise_authorise() */ /* */ /* Accepts changes in an authorisation dialogue */ /* and proceeds with the authorisation request. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int authorise_authorise(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { char host [Limits_HostName]; char username [Limits_AuthUserWrit]; char password [Limits_AuthPassWrit]; _kernel_oserror * e; browser_data * b = (browser_data *) handle; /* Read the user name and password from the authorisation dialogue */ ChkError(writablefield_get_value(0, authorise_dbox, AuthUserWrit, username, sizeof(username), NULL)); ChkError(writablefield_get_value(0, authorise_dbox, AuthPassWrit, password, sizeof(password), NULL)); username[sizeof(username) - 1] = 0; password[sizeof(password) - 1] = 0; /* Work out the host name */ urlutils_host_name_from_url(browser_fetch_url(b), host, sizeof(host)); /* Try to remember the authentication details. Note that the current_realm */ /* contents are taken to be correct as the realm for this request; this */ /* will always be OK at the time of writing the code as only one request */ /* can ever be in progress, but if this should change the code will need */ /* to be altered to cope. Can't see why it would though; the user only has */ /* the one pair of hands and (usually) the one keyboard. Programming for */ /* aliens, perhaps...? */ ChkError(authorise_remember(host, current_realm, username, password)); /* Can get rid of the authorisation dialogue now */ ChkError(event_deregister_toolbox_handlers_for_object(authorise_dbox)); /* If there was a null handler for the dialogue, remove it */ { int temp_type; if ( controls.dbox_anims && !gadget_get_type(0, authorise_dbox, StatusBarAnimAnim, &temp_type) ) deregister_null_claimant(Wimp_ENull, toolbars_animate_slow, (browser_data *) authorise_dbox); } /* Can't use ChkError now; instead must run through fetch_authorisation_fail, */ /* or the user could be left stuck in authentication mode. */ e = toolbox_delete_object(0, authorise_dbox); if (e) { fetch_authorisation_fail(b); return 1; } authorise_dbox = 0; /* Call the fetcher's routines to proceed, and exit */ fetch_authorisation_proceed(b, NULL, current_realm, browser_fetch_url(b)); return 1; } /*************************************************/ /* authorise_cancel() */ /* */ /* Discards any changes in an authorisation */ /* dialogue, deleting it if Adjust isn't being */ /* used at the time. If the dialogue is deleted */ /* the appropriate steps are taken to indicate */ /* authorisation failure to the user. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ int authorise_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { WimpGetPointerInfoBlock info; if (!authorise_dbox) return 0; ChkError(wimp_get_pointer_info(&info)); if (info.button_state & Wimp_MouseButtonAdjust) { /* Clear the password and user name writables */ ChkError(writablefield_set_value(0, authorise_dbox, AuthPassWrit, "")); ChkError(writablefield_set_value(0, authorise_dbox, AuthUserWrit, "")); } else { /* Deregister event handlers and delete the dialogue. Note */ /* the use of authorise_dbox rather than idb->self_id; if */ /* this handler managed to get called on some odd ID or */ /* or with a null ID block it'll still work. */ ChkError(event_deregister_toolbox_handlers_for_object(authorise_dbox)); /* If there was a null handler for the dialogue, remove it */ { int temp_type; if ( controls.dbox_anims && !gadget_get_type(0, authorise_dbox, StatusBarAnimAnim, &temp_type) ) deregister_null_claimant(Wimp_ENull, toolbars_animate_slow, (void *) authorise_dbox); } /* No error check; want to drop through to the fail routine */ /* regardless. */ toolbox_delete_object(0, authorise_dbox); authorise_dbox = 0; fetch_authorisation_fail((void *) handle); } return 1; } /*************************************************/ /* authorise_read_realm() */ /* */ /* Given a pointer to a string holding an */ /* appropriate part of an HTTP response header */ /* detailing the realm for an authentication */ /* request, extract the realm and put it into */ /* the local static char array 'current_realm'. */ /* */ /* Parameters: Pointer to the header entry */ /* string. */ /* */ /* Returns: Pointer to the current_realm */ /* string. */ /*************************************************/ char * authorise_read_realm(char * header_entry) { char * p = header_entry; /* The realm lies between two double quotes */ while (*p && *p != '"') p++; if (*p) p++; StrNCpy0(current_realm, p); p = current_realm; while(*p && *p != '"') p++; *p = 0; return current_realm; } /*************************************************/ /* authorise_find_offset() */ /* */ /* Returns the offset of the authorisation data */ /* for a given realm and host, or -1 if none is */ /* present / can be found. */ /* */ /* Parameters: Pointer to the host string; */ /* */ /* Pointer to the realm string. */ /* */ /* Returns: Offset into the 'authorise' flex */ /* block that the given entry lies */ /* at, or -1 if not found. */ /*************************************************/ int authorise_find_offset(char * host, char * realm) { int o, s, l; /* Only proceed if there's allocated data to look at! */ if (authorise) { o = 0; s = flex_size((flex_ptr) &authorise); while (o < s) { l = strlen(authorise + o) + 1; /* Length of host string */ /* If the host and realm match, return the current offset */ if (!strcmp(authorise + o, host) && !strcmp(authorise + o + l, realm)) return o; /* Skip o past the host string and find the length of the realm string */ o += l; l = strlen(authorise + o) + 1; /* Skip o past the realm string and find the length of the user name string */ o += l; l = strlen(authorise + o) + 1; /* Skip o past the realm string and find the length of the password string */ o += l; l = strlen(authorise + o) + 1; /* Skip o past the password string to get to the next entry */ o += l; } } /* Failed to find the given host / realm, so return -1 */ return -1; } /*************************************************/ /* authorise_find_user_name() */ /* */ /* Returns an offset into the 'authorise' flex */ /* block at which the user name for a given host */ /* and realm lies, or -1 for not found. */ /* */ /* Parameters: Pointer to the host string; */ /* */ /* Pointer to the realm string. */ /* */ /* Returns: Offset into the 'authorise' flex */ /* block that the user name lies at, */ /* or -1 if not found. */ /*************************************************/ int authorise_find_user_name(char * host, char * realm) { int o; o = authorise_find_offset(host, realm); if (o < 0) return -1; o += strlen(authorise + o) + 1; /* Skip over host */ o += strlen(authorise + o) + 1; /* Skip over realm */ return o; } /*************************************************/ /* authorise_find_password() */ /* */ /* Returns an offset into the 'authorise' flex */ /* block at which the password for a given host */ /* and realm lies, or -1 for not found. */ /* */ /* Parameters: Pointer to the host string; */ /* */ /* Pointer to the realm string. */ /* */ /* Returns: Offset into the 'authorise' flex */ /* block that the password list at, */ /* or -1 if not found. */ /*************************************************/ int authorise_find_password(char * host, char * realm) { int o; o = authorise_find_offset(host, realm); if (o < 0) return -1; o += strlen(authorise + o) + 1; /* Skip over host */ o += strlen(authorise + o) + 1; /* Skip over realm */ o += strlen(authorise + o) + 1; /* Skip over user name */ return o; } /*************************************************/ /* authorise_remember() */ /* */ /* Stores authorisation data in the 'authorise' */ /* flex block. */ /* */ /* Parameters: Pointer to the host string; */ /* */ /* Pointer to the realm string; */ /* */ /* Pointer to the user name string; */ /* */ /* Pointer to the password string. */ /*************************************************/ _kernel_oserror * authorise_remember(char * host, char * realm, char * username, char * password) { int s, n, ok; /* Don't want to store something twice... */ authorise_forget(host, realm); /* n is the entry length for the data, including terminators */ n = strlen(host) + 1 + strlen(realm) + 1 + strlen(username) + 1 +strlen(password) + 1; /* Allocate memory for the entry */ if (authorise) s = flex_size((flex_ptr) &authorise); else s = 0; if (s) ok = flex_extend((flex_ptr) &authorise, s + n); else ok = flex_alloc ((flex_ptr) &authorise, n); if (!ok) return make_no_fetch_memory_error(11); /* Copy the data in place */ strcpy(authorise + s, host); s += strlen(authorise + s) + 1; strcpy(authorise + s, realm); s += strlen(authorise + s) + 1; strcpy(authorise + s, username); s += strlen(authorise + s) + 1; strcpy(authorise + s, password); return(NULL); } /*************************************************/ /* authorise_forget() */ /* */ /* Discards remembered user name and password */ /* data for a given host and realm. */ /* */ /* Parameters: Pointer to the host string; */ /* */ /* Pointer to the realm string. */ /*************************************************/ void authorise_forget(char * host, char * realm) { int o; o = authorise_find_offset(host, realm); /* Only proceed if there does seem to be an entry for this host and realm */ if (o >= 0) { int l, s, n; s = flex_size((flex_ptr) &authorise); n = 0; l = strlen(authorise + o) + 1; n += l; o += l; /* Skip host */ l = strlen(authorise + o) + 1; n += l; o += l; /* Skip realm */ l = strlen(authorise + o) + 1; n += l; o += l; /* Skip user name */ l = strlen(authorise + o) + 1; n += l; o += l; /* Skip password */ /* If the entry above was as long or apparently longer than the flex block */ /* itself, free the whole block. */ if (n >= s) { flex_free((flex_ptr) &authorise); authorise = NULL; } /* Otherwise, move data above the entry down over it and shrink the flex block */ else { memmove(authorise + o, authorise + o + n, s - o - n); flex_extend((flex_ptr) &authorise, s - n); } } }