/* 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 : Fetch.c */ /* Purpose: Low-level page fetch related functions */ /* functions (as opposed to fetch.c where */ /* all the higher level stuff goes on) */ /* Author : A.D.Hodgkinson */ /* History: 25-Nov-96: Created */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "swis.h" #include "flex.h" #include "HTMLLib.h" /* HTML library API, Which will include html2_ext.h, tags.h and struct.h */ #include "wimp.h" #include "wimplib.h" #include "event.h" #include "svcprint.h" #include "Global.h" #include "MiscDefs.h" #include "TBEvents.h" #include "Utils.h" #include "Authorise.h" #include "Browser.h" #include "Cookies.h" #include "FontManage.h" #include "Forms.h" #include "Frames.h" #include "History.h" #include "Images.h" #include "Memory.h" #include "Meta.h" #include "Redraw.h" #include "Reformat.h" #include "Toolbars.h" #include "URLutils.h" #include "Windows.h" #include "Fetch.h" /* Globals */ int authorising = 0; /* Authorisation for a fetch is in progress */ /* Statics */ static urlstat * fetch_list = NULL; /* Points to the head of the linked list of structures. */ static char * fetch_buffer = NULL; /* For html_get_next_token to shovel in data. */ /* Local definitons */ #define FetchBufferSize 8192 /* Buffer for getting data from the URL module in html_get_next_token. */ #define AuthorisationStr "Authorization: Basic " /* Local compilation options */ #define FRAMES_SUPPORT #undef DUMP_HEADERS /* Static function prototypes */ static HStream * fetch_find_anchor_token_r (browser_data * b, HStream * streambase, char * anchor); /*************************************************/ /* fetch_start() */ /* */ /* Initiate a fetch from some URL. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* for the browser window that the */ /* fetch relates to; the URL is */ /* pointed to in that structure, as */ /* the last item in the history. */ /* */ /* Returns: A pointer to a _kernel_oserror */ /* structure if an error occured, or */ /* NULL if there was no error. */ /*************************************************/ _kernel_oserror * fetch_start(browser_data * b) { int handle, method; _kernel_oserror * e; #ifdef TRACE if (tl & (1u<<6)) Printf("\nfetch_start: Called\n"); #endif /* (Order of evaluation ensures the check for the contents of */ /* the memory pointed to by browser_fetch_url only occurs if */ /* the pointer isn't null) */ if (!browser_fetch_url(b) || !*browser_fetch_url(b)) { b->fetch_status = BS_IDLE; toolbars_cancel_status(b, Toolbars_Status_Fetching); return NULL; } /* URL method is set to POST if there is forms data, or GET */ /* if not (see Fetch.c for the definitions) */ method = b->extradata ? URL_Method_http_POST : URL_Method_http_GET; /* Find out if this is an internal URL, and if so, */ /* set the 'displayed' field in the browser_data */ /* struct appropriately. */ urlutils_set_displayed(b, b->urlfdata); /* Get, and start parsing the document */ e = html_get(b->urlfdata, /* Required document */ b->extradata, /* Extra bits to append for POST etc */ &handle, /* The library's handle for request */ method, /* See above - POST or GET at this point */ NULL, /* User name for Mailserv */ 1, /* Allow HTML parsing, 1 = yes, 0 = no */ 0); /* If 1, don't go through a proxy - e.g. for a reload */ #ifdef TRACE if (b->extradata) { flexcount -= flex_size((flex_ptr) &b->extradata); if (tl & (1u<<13)) Printf("** flexcount: %d\n",flexcount); } #endif if (b->extradata) flex_free((flex_ptr) &b->extradata); if (e) { b->fetch_status = BS_IDLE; toolbars_cancel_status(b, Toolbars_Status_Fetching); return e; } /* No error, so signal that the fetch has started. */ b->fetch_handle = handle; b->fetch_status = BS_STARTED; toolbars_update_status(b, Toolbars_Status_Fetching); /* At this point e will always be NULL but that might change, */ /* so the full trace code is being left in for now */ #ifdef TRACE if (tl & (1u<<6)) { if (e) Printf("fetch_start: Exiting with error\n"); else Printf("fetch_start: Successful\n"); } #endif return e; } /*************************************************/ /* fetch_fetching() */ /* */ /* Returns 1 if there is a fetch in progress */ /* according to the contents of the data that */ /* was pointed to (see Parameters), else 0. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the inquiry. */ /* */ /* Returns: 1 if a fetch is in progress; */ /* 0 if a fetch is not in progress. */ /*************************************************/ int fetch_fetching(browser_data * b) { /* This is currently very simple - a fetch is considered to be in */ /* progress so long as the fetch_status doesn't indicate BS_IDLE. */ return (b->fetch_status != BS_IDLE); } /*************************************************/ /* fetch_token_data_address() */ /* */ /* Returns a pointer to data associated with a */ /* token, if it holds text, or NULL if that data */ /* means something else. (A token is an HStream */ /* structure - see HTMLLib:struct.h). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* associated with the token; */ /* */ /* Pointer to the token. */ /* */ /* Returns: Pointer to the data if it is */ /* text, else NULL. */ /*************************************************/ void * fetch_token_data_address(browser_data * b, HStream * token) { /* No data for horizontal rules or images */ if (token->style & HR) return NULL; if (token->style & IMG) return NULL; if ((token->style & INPUT) && HtmlINPUTtype(token) == inputtype_IMAGE) return NULL; /* Otherwise, return a pointer to the text */ return token->text; } /*************************************************/ /* fetch_find_name_tag() */ /* */ /* Finds the # separating an anchor name in a */ /* URL. */ /* */ /* Parameters: A pointer to the URL string. */ /* */ /* Returns: A pointer to the anchor string, */ /* including the leading # */ /*************************************************/ char * fetch_find_name_tag(char * url) { char * p; p = strchr(url,'/'); /* Get past the first /, as in http:/ */ if (p) p = strchr(p + 1, '/'); /* Get past second /, as in http:// */ if (p) p = strchr(p + 1, '/'); /* Get past site specifier, as in http://www.this.that/ */ if (p) p = strchr(p + 1, '#'); /* Find # in the document path */ return p; } /*************************************************/ /* fetch_find_anchor_token() */ /* */ /* Returns the address of the first token in the */ /* token list which has the given anchor name */ /* associated with it, or NULL if none can be */ /* found. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token list; */ /* */ /* Pointer to the anchor name. */ /* */ /* Returns: Pointer to the token associated */ /* with the given anchor name, or */ /* NULL if none is found. */ /*************************************************/ HStream * fetch_find_anchor_token(browser_data * b, char * anchor) { return fetch_find_anchor_token_r(b, b->stream, anchor); } /*************************************************/ /* fetch_find_anchor_token_r() */ /* */ /* Recursive back-end to fetch_find_anchor_token */ /* - takes an extra parameter giving the top of */ /* the HStream list to scan. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the token list; */ /* */ /* Pointer to first item in HStream */ /* list to scan; */ /* */ /* Pointer to the anchor name. */ /* */ /* Returns: As fetch_find_anchor_token. */ /*************************************************/ static HStream * fetch_find_anchor_token_r(browser_data * b, HStream * streambase, char * anchor) { HStream * tp; tp = streambase; /* Go down the token list, checking if a token represents an */ /* anchor, has a name, and that name matches the given one. If */ /* so, return the token address, else go onto the next token. */ while (tp && (tp->flags & HFlags_DealtWithToken)) { /* A table token? */ if ( tp->tag == TABLE && ISBODY(tp) ) { table_stream * table = (table_stream *) tp; table_row * row = NULL; table_headdata * head = NULL; HStream * tf; int cellcount = 0; int cellmax = table->ColSpan * table->RowSpan; /* Scan the table for the token, using a recursive */ /* call to this function for each cell. */ if (table->cells) { row = table->List; while (row && cellcount < cellmax) { head = row->List; while (head && cellcount < cellmax) { switch (head->Tag) { case TagTableData: case TagTableHead: { tf = fetch_find_anchor_token_r(b, (HStream *) head->List, anchor); if (tf) return tf; } break; } cellcount ++; head = head->Next; /* Closure of 'while (head && ...)' */ } row = row->Next; /* Closure of 'while (row && ...)' */ } /* Closure of 'if (table->cells)' */ } /* Closure of check to see if token represents a table */ } else if ( (tp->style & A) && tp->name && !strcmp(tp->name, anchor) ) return tp; tp = tp->next; } /* No match found - return NULL. */ return NULL; } /*************************************************/ /* fetch_preprocess_token() */ /* */ /* Takes a token for a given browser_data struct */ /* and preprocesses it - e.g. tells the image */ /* library about image tokens so fetches can */ /* start for those images. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure relevant to the token; */ /* */ /* Pointer to the token. */ /*************************************************/ void fetch_preprocess_token(browser_data * b, HStream * tptr) { int reprocess_table = 0; /* finaltoken keeps track of the last token dealt with by this */ /* routine, in the main token stream. */ if (!tptr->parent) b->finaltoken = tptr; /* Are we reprocessing the contents of a table tag which has been */ /* dealt with before? */ if (tptr->tag == TABLE && ISBODY(tptr)) reprocess_table = 1; /* Deal with smart quotes etc. */ // This could have side effects placed here...! Sort it out! - the function just returns at present. reformat_change_text(b, tptr); /* Deal with document body tags (not within HEAD, FRAMESET etc. containers) */ if (ISBODY(tptr)) { /* Don't reprocess the token generally - this may accidentally */ /* clear formflag, say, if the high level table structures */ /* we are passing through on the way to the lower level ones */ /* don't have the FORM bit set. Other such consequences are */ /* avoided by only doing the table handling if this is a table */ /* tag with the HFlags_DealtWithToken bit set in its flags. */ if (!reprocess_table) { /* If the 'style' entry has the image (IMG) bit set, ask */ /* the image library to handle a new image. tptr->src */ /* will be a char * to the URL of the image. */ if (tptr->style & IMG) { if (fetch_chkerror(b, image_new_image(b, tptr->src ? tptr->src : "", tptr, 0))) return; } /* Handle some form tags */ if (tptr->style & FORM) { /* If there are no forms in this fetch so far, create a new one */ if (!b->formflag) { if (fetch_chkerror(b, form_new_form(b, tptr))) return; } /* Deal with creating a new field as appropriate */ if (tptr->style & INPUT) { switch(HtmlINPUTtype(tptr)) { case inputtype_TEXT: if (fetch_chkerror(b, form_new_field(b, tptr, form_text, tptr->text ))) return; break; case inputtype_PASSWORD:if (fetch_chkerror(b, form_new_field(b, tptr, form_password, tptr->text ))) return; break; case inputtype_CHECKBOX:if (fetch_chkerror(b, form_new_field(b, tptr, form_checkbox, (char *) HtmlINPUTchecked(tptr)))) return; break; case inputtype_RADIO: if (fetch_chkerror(b, form_new_field(b, tptr, form_radio, (char *) HtmlINPUTchecked(tptr)))) return; break; case inputtype_IMAGE: if (fetch_chkerror(b, form_new_field(b, tptr, form_image, NULL ))) return; break; case inputtype_HIDDEN: if (fetch_chkerror(b, form_new_field(b, tptr, form_hidden, NULL ))) return; break; case inputtype_SUBMIT: if (fetch_chkerror(b, form_new_field(b, tptr, form_submit, NULL ))) return; break; case inputtype_RESET: if (fetch_chkerror(b, form_new_field(b, tptr, form_reset, NULL ))) return; break; } } /* Handle text areas */ if ((tptr->style) & TEXTAREA) { if (fetch_chkerror(b, form_new_field(b, tptr, form_textarea, tptr->text))) return; } /* Handle selection buttons */ if ((tptr->style) & SELECT) { if (fetch_chkerror(b, form_new_field(b, tptr, form_select, (char *) HtmlSELECToptions(tptr)))) return; } b->formflag = 1; } else b->formflag = 0; } /* Tables - need to preprocess any HStreams attached as part of a table */ /* tag. Because any one token is only run through this preprocessor */ /* once, and because when this table tag is run through it all of the */ /* HStreams within may not have arrived yet (the page is only partially */ /* fetched), it is still necessary to rescan the attached HStreams at a */ /* later date (e.g. as part of the reformatting process) to ensure they */ /* are all preprocessed correctly. */ if (tptr->tag == TABLE) { table_stream * table = (table_stream *) tptr; table_row * R; table_headdata * D; HStream * attached; R = table->List; /* Scan the rows and cells */ while (R) { D = R->List; while (D) { if (D->Tag) { switch (D->Tag) { case TagTableData: case TagTableHead: { attached = (HStream *) D->List; /* Preprocess any attached HStream list - must */ /* check table tags even if they've been done */ /* before to look for new HStreams, otherwise */ /* avoid preprocessing the same thing twice. */ while (attached) { if ( ( ISBODY(attached) && attached->tag == TABLE ) || ( !(attached->flags & HFlags_DealtWithToken) ) ) fetch_preprocess_token(b, attached); attached = attached->next; } } break; } } D = D->Next; } R = R->Next; } } /* Closure of long 'if' to see if the HStream structure represented */ /* a body tag or header information - the code is run if it's a */ /* body tag. */ } #ifdef FRAMES_SUPPORT else if ISFRAMESET(tptr) { browser_data * parent; browser_data * child = b; parent = b->parent; if (!parent) parent = b; if (tptr->size) { int level = tptr->size; /* If filling_frame is equal to the number of children, then */ /* they've all been filled - the frameset must be broken. */ if ( !child->nchildren || ( child->nchildren && child->filling_frame < child->nchildren ) ) { while (level > 1) { /* If in a nested frameset, find out what browser_data struct */ /* to put the frames in. This should go in the next frame that */ /* is to be filled in according to the parent. */ child = (browser_data *) child->children[child->filling_frame]; level--; } /* If stepping down a level, i.e. after a /frameset tag, */ /* will want to increment the filling_frame field for */ /* this browser to say that the child we just stepped */ /* down for has been filled with a frameset. There's the */ /* complication of a /frameset being followed by another */ /* frameset and the level therefore staying the same; */ /* this is dealt with in the frameset section below. */ if (tptr->size < parent->nesting_level) { child->filling_frame++; } /* The aforementioned frameset section... */ if (!(tptr->style & FRAME)) { /* Define a new frameset. */ if (tptr->size == parent->nesting_level && child->parent) { /* If at the same level as before on receiving a frameset */ /* tag, must be doing nested frames and just had a */ /* /frameset before this tag came along. So need to */ /* increment the filling_frame counter of the *parent* */ /* (remember, we're at the level of the frame to fill in, */ /* not in the level below as with the code above that */ /* checked the level had stepped down). Therefore, need */ /* to find out again what browser_data struct is to be */ /* given the frameset based on the new filled_frame value. */ child->parent->filling_frame++; child = (browser_data *) child->parent->children[child->parent->filling_frame]; } /* Must force scrollbars off in this current view, */ /* as a frameset is about to appear over it. */ windows_check_tools(child, NULL); /* Finally, define the frameset at the required depth. */ frames_define_frameset(child, tptr); } else { /* Fill in details of a frame. */ frames_define_frame(child, tptr); } parent->nesting_level = tptr->size; } #ifdef STRICT_PARSER else { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("FramNest:Frames definition is badly nested; could not complete the frames layout.", 0,0)); show_error_ret(&erb); } #endif } } #endif else if ISHEAD(tptr) { /* Deal with header (HEAD) tags */ if ((tptr->style & TITLE) && tptr->text) { /* The tag is TITLE, and there is title text. */ char title[MaxTiBLen - 1]; char * p = title; char * end; /* Can't overflow maximum length so just crop the string to fit */ StrNCpy0(title, tptr->text); /* Strip any spaces at the start */ while (*p == ' ') p++; /* Strip any spaces at the end */ end = (char *) ((int) p + strlen(p) - 1); if (end > p) while (*end == ' ') *end-- = 0; /* If there's anything left now... */ if (*p != 0) { /* Set the title */ if (!b->ancestor && fetch_chkerror(b, window_set_title(0, b->self_id, p))) return; /* Try adding this title to the history, ignoring any errors */ history_add_title(p, browser_fetch_url(b)); } } if (tptr->tag == BODY) { /* The BODY tag. All sorts of exciting stuff in here... */ if (HtmlBODYbackground(tptr)) { /* If there's a URL for the image, ask the image library for it */ /* and remember the image number in the browser_data structure */ image_new_image(b, HtmlBODYbackground(tptr), tptr, 2); } /* Get the 24-bit background colour, if any. */ if (HtmlBODYbgcolour(tptr) != NULL_COLOUR) { b->backgroundcol = HtmlBODYbgcolour(tptr); #ifdef TRACE if (tl & (1u<<6)) Printf("fetch_preprocess_token: Background colour set to %d\n", b->backgroundcol); #endif /* If there's no actual background image, set the anti-alias */ /* colour to be the same as the background colour. */ if (b->backimage < 0) b->aacol = b->backgroundcol; browser_update_bottom(b, 0); } /* Get the rest of the colour info out. */ if (HtmlBODYtext (tptr) != NULL_COLOUR) b->textcol = HtmlBODYtext (tptr); if (HtmlBODYlink (tptr) != NULL_COLOUR) b->linkcol = HtmlBODYlink (tptr); if (HtmlBODYvlink(tptr) != NULL_COLOUR) b->usedcol = HtmlBODYvlink(tptr); if (HtmlBODYalink(tptr) != NULL_COLOUR) b->follcol = HtmlBODYalink(tptr); /* Also pull out the onload and onunload scripts. */ if (HtmlBODYonload (tptr)) b->onload = HtmlBODYonload (tptr); if (HtmlBODYonunload(tptr)) b->onunload = HtmlBODYonunload(tptr); } /* Deal with META... tags */ if (tptr->tag == META) { meta_process_tag(b, tptr); } /* Closure of long else to see if the HStream structure represented */ /* a body tag or header information - the code is run if it's a */ /* head tag. */ } /* If we've reached here, the token has been dealt with */ /* successfully - so mark this in its flags word. */ tptr->flags |= HFlags_DealtWithToken; return; } /*************************************************/ /* fetch_fetcher() */ /* */ /* The main part of the fetch routine. Handles */ /* the processing of data from the URL module, */ /* after fetch_start() has asked it to start */ /* getting data from a server. */ /* */ /* Parameters: A pointer to a browser_data */ /* structure, to which the fetch */ /* relates. */ /*************************************************/ void fetch_fetcher(browser_data * b) { HStream * tptr; int start = -1; int i, remain, sofar, waiting; for (i = 0; i < 20; i ++) /* Get several tokens on each null event */ { /* For BS_DATAFETCH, save the data to a file */ if (b->fetch_status == BS_DATAFETCH) { #ifdef TRACE if (tl & (1u<<6)) Printf("fetch_fetcher: fetch_status = BS_DATAFETCH.\n"); #endif /* This code gets called by the stuff further down advancing */ /* the status to BS_DATAFETCH. */ if (b->savefile) /* Proceed if there's a file to save to */ { char buffer[4096]; int success, bytes, done; _kernel_oserror * e; /* Get a chunk of data */ // Printf("savefile\n"); e = html_get_next_chunk(NULL, b->fetch_handle, buffer, sizeof(buffer), &done, &bytes); success = !e; // Printf("success, bytes, done: %d, %d, %d\n",success,bytes,done); /* If there's an error, show it but continue */ if (!success) show_error_ret(e); /* If there was not an error, write a chunk of file */ if (success && bytes) success = fwrite(buffer, 1, bytes, b->savefile); /* If the expected number of bytes was not written, */ /* show whatever error fwrite generated */ if (success != bytes && bytes) { success = 0; show_error_ret(_kernel_last_oserror()); } /* If apparently successful and finished, read the pathname */ /* of the file so the filetype can be set. */ if (success && done) { _swix(OS_Args, _INR(0,3), 7, /* Read pathname of open file */ b->savefile->__file, buffer, sizeof(buffer)); } /* If finished or there was some error above, stop the fetch */ if (!success || done) fetch_stop(b, 1); /* This closes the output file, too */ /* If successful and finished, set the filetype */ if (success && done) { _swix(OS_File, _INR(0,2), 18, buffer, b->savetype); } /* Finally, ensure toolbars are up to date. */ toolbars_update_progress(b); } } #ifdef TRACE if (tl & (1u<<6)) Printf("fetch_fetcher: Get next token\n"); #endif /* Get the next token, with fetch_chkerror allowing us to exit */ /* relatively cleanly should an error occur. */ if ( fetch_chkerror( b, html_get_next_token( b, b->fetch_handle, &remain, &sofar, &tptr, &waiting, (flex_ptr) &b->source, browser_fetch_url(b), 0 ) ) ) return; /* Show the fetch's progress */ toolbars_update_progress(b); /* If waiting = 3 the data being fetched isn't parseable. Alternatively, */ /* if savelink is set, there is source present and the last token was */ /* fetched OK (so waiting = 0), then treat the data as non parseable - */ /* i.e., open a save dialogue for the data. */ if (waiting == 3 || (b->savelink && b->source && !waiting)) { // char pathname[256]; #ifdef TRACE if (tl & (1u<<6)) Printf("fetch_fetcher: fetch_status moved to BS_DATAFETCH\n"); #endif // b->fetch_status = BS_DATAFETCH; // b->savetype = remain; // // if ( // waiting == 3 || // ( // b->savelink && // b->source && // !waiting // ) // ) // { // char pathname[256]; // // b->status_fetch = BS_DATAFETCH; // b->savetype = remn; // // fetch_build_leaf(browser_fetching_url(b), // pathname, // sizeof(pathname)); // // if ( // !saveas(remn, // pathname, // -1, // fetch_save_url, // NULL, // NULL, // (void *) b) // // || !b->savefile // ) // fetch_stop(b, TRUE); // // return; // } // And do everything else; or for now, say sorry... if (choices.full_screen || 1) { fetch_stop(b, 1); StrNCpy0(erb.errmess, lookup_token("NotInline:Sorry, can't handle this data format...",0,0)); erb.errnum = Utils_Error_Custom_Message; show_error_ret(&erb); } else { b->fetch_status = BS_DATAFETCH; b->savetype = remain; b->savelink = 1; b->savefile=fopen(":4.$.Test","wb"); } return; } /* If waiting = 2, a redirect has occurred */ else if (waiting == 2) { char * url; int internal = 0; #ifdef TRACE if (tl & (1u<<6)) Printf("fetch_fetcher: Redirect to %s\n",(char *) remain); #endif /* Get the new URL pointed to by 'url' */ url = (char *) remain; if (b->displayed != Display_Fetched_Page) internal = 1; /* Record the pre-redirection URL in the global history */ if (!internal) { history_record_global(browser_fetch_url(b)); /* Allocate space for new URL and copy it into that space */ #ifdef TRACE if (tl & (1u<<12)) Printf("fetch_fetcher: Chunk CK_FURL set to %d\n",strlen(url) + 1); #endif if (fetch_chkerror(b, memory_set_chunk_size(b, NULL, CK_FURL, strlen(url) + 1))) return; strcpy(b->urlfdata, url); } else { char furl[2048]; /* Allocate space for new URL plus old URL and separator, */ /* and copy them into that space */ #ifdef TRACE if (tl & (1u<<12)) Printf("fetch_fetcher: Chunk CK_FURL set to %d\n",strlen(url) + strlen(furl) + 2); #endif StrNCpy0(furl, browser_fetch_url(b)); if (fetch_chkerror(b, memory_set_chunk_size(b, NULL, CK_FURL, strlen(url) + strlen(furl) + 2))) return; strcpy(b->urlfdata, url); strcat(b->urlfdata, ":"); strcat(b->urlfdata, furl); } /* Reflect the new URL in the status and URL bars */ toolbars_update_status(b, Toolbars_Status_Fetching); toolbars_update_url(b); } else if (!waiting) { /* If it isn't already non-zero, set 'start' to the number of */ /* the last line in the line list */ if (start < 0) start = b->cell->nlines - 1; /* We're not waiting, have we got a token? */ if (b->fetch_status == BS_STARTED) { /* Yes - this is the first token on this page. Get the window */ /* ready for the new page - this includes ditching old data. */ int l; /* Make the current display URL = current fetch URL... */ l = strlen(browser_fetch_url(b)); /* Get the fetching URL string length */ /* Allocate memory for it, and copy the string across */ #ifdef TRACE if (tl & (1u<<12)) Printf("fetch_fetcher: Chunk CK_DURL set to %d\n",l + 1); #endif if (fetch_chkerror(b, memory_set_chunk_size(b, NULL, CK_DURL, l + 1))) return; strcpy(b->urlddata, browser_fetch_url(b)); toolbars_hide_internal(b->urlddata); /* Update the title bar */ if (!b->ancestor) /* Child windows don't have title bars... */ { char title[MaxTiBLen - 1]; StrNCpy0(title, browser_current_url(b)); if (fetch_chkerror(b, window_set_title(0, b->self_id, title))) return; } /* Write to the global history */ history_record_global(browser_fetch_url(b)); /* Set status to FETCHING instead of STARTED */ b->fetch_status = BS_FETCHING; /* If there was previous display data present, get rid of it */ if ((b->display_handle) && (b->display_handle != b->fetch_handle)) { html_close(b->display_handle); b->display_handle = 0; } /* Signal that the display data is coming from the fetch data, */ /* so that the fetch data doesn't get accidentally ditched */ /* until it's finished with */ b->display_handle = b->fetch_handle; /* Initialise various things inside the browser_data structure */ /* to do with colours and so-forth */ #ifdef TRACE if (tl & (1u<<18)) Printf("New fetch for %p, stream %p\n",b,tptr); #endif b->stream = tptr; /* Pointer to list of HStream structures */ b->finaltoken = NULL; /* Last HStream structure dealt with */ b->lastchar = ' '; /* Last character dealt with */ b->backgroundcol = -1; /* Background colour, or -1 for default */ b->backimage = -1; /* Image no. of background image, 0=none */ b->textcol = choices.col_text; /* Body text default colour */ b->linkcol = choices.col_link; /* Link text default colour */ b->usedcol = choices.col_used; /* Followed link default colour */ b->aacol = redraw_backcol(b); /* Colour to anti-alias to, or -1=none */ b->follcol = choices.col_foll; /* Following link default colour */ b->selecol = choices.col_sele; /* Selected (highlighted) link colour */ b->onload = NULL; /* <BODY onload> attribute */ b->onunload = NULL; /* <BODY onunload> attribute */ /* Ensure the nesting level and filling frame counters are reset */ b->nesting_level = 0; b->filling_frame = 0; /* Cancel any pending automatic fetches */ if (b->meta_refresh_at) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) meta_check_refresh, b); b->meta_refresh_at = 0; b->meta_refresh_url = NULL; /* Cancel pending reformats */ reformat_stop_pending(b); /* Hideously long comment alert... */ /* */ /* Although if a frame loads a document containing another frameset */ /* this is in one sense a nested frame defintion, in another the */ /* second document is independent of the first; certainly as far as */ /* incrementing the filling_frame field of the parent goes, the */ /* <frame> tag that loaded this document into the frame in the */ /* first place will already have done that. */ /* */ /* Consequently, whilst all child frames have an ancestor - the */ /* original, base browser that defined the first of possibly many */ /* framesets - only genuinely nested frameset arrays have parents. */ /* That is, a parent can only have children; it may not also be a */ /* child (i.e. have a parent), it may only have an ancestor. */ /* */ /* Genuinely nested frames consist of one document with more than */ /* one set of <frameset> tags. Here, filling_frame considerations */ /* demand the use of a nested_level count and a parent as well as */ /* an ancestor. For those single documents, we won't be running */ /* this code when second or further framesets come in, so the */ /* parent field will get estabilshed and remain as long as needed */ /* by the frames routines. */ b->parent = NULL; /* Yup - that whole comment for one tiny line of code. Woo... */ /* */ /* 'Course, that said, it's useful for every child to know who its */ /* parent is. That's what the real_parent field is for. */ // { // WimpGetWindowStateBlock state; // // state.window_handle = b->window_handle; // // if (!wimp_get_window_state(&state)) // { // b->display_width = b->display_extent = state.visible_area.xmax - state.visible_area.xmin; // } // } /* Don't want to set the pointer_over field, as then it may not seem to */ /* have changed from one fetch to another; the pointer can get 'stuck' */ /* in the 'link' shape. */ b->highlight = NULL; /* No tokens are highlighted */ b->selected = NULL; /* No tokens are selected */ b->selected_owner = NULL; #ifdef TRACE if (tl & (1u<<6)) Printf("\nfetch_fetcher: Document colours etc. set to default values\n"); #endif /* Clear the status bar contents block for an ancestor */ /* window beginning a new fetch. */ if (!b->ancestor && b->nstatus) { #ifdef TRACE if ( (tl & (1u<<1)) || (tl & (1u<<6)) ) Printf("fetch_fetcher: Freeing status_contents array\n"); #endif b->nstatus = 0; memory_set_chunk_size(b, NULL, CK_STAT, 0); } /* Clear allocated memory for the forms, and tell */ /* the font library that the fonts aren't needed */ // /* anymore. Images are cleared after the fetch, */ // /* so that any images common between the two can */ // /* be preserved. */ form_discard(b); fm_lose_fonts(b); /* Flag that images need to be garbage collected later */ // Um... ToDo list time... image_discard(b); // b->clear_images = 1; /* IMPORTANT, must call the reformatter here to ensure that all various */ /* line list data is invalidated, discarded, and any new stuff is valid. */ /* Otherwise, could have bits of the application subsequently using old */ /* line data and things will go very wrong very quickly. */ /* */ /* DON'T put anything that might try and read line data before this call! */ b->display_extent = b->display_width; /* Ensure a new fetch starts with the horizontal extent matching the visible area */ reformat_format_from(b, -1, 1, -1); reformat_check_extent(b); /* Collapse any frames within this browser */ frames_collapse_set(b); /* Ensure window tools are up to date */ if (b->ancestor || b->full_screen) windows_set_tools(b, NULL, !b->ancestor, 0, 0, 0); /* If there's a # inside the URL (i.e. we're supposed to jump to an */ /* anchor) then set the token to display first to be DISPLAY_NAMED, */ /* a large number which acts as a flag to say 'jump to anchor'. The */ /* fetch polling routine (see FetchPage.c) should notice this and */ /* start looking for a token with the appropriate name, and if it */ /* finds it, display that token. */ if (fetch_find_name_tag(browser_current_url(b))) b->display_request = DISPLAY_NAMED; /* Ensure the pointer shape is correct */ browser_pointer_check(0, NULL, NULL, b); /* Reflect the new browser status */ toolbars_update_status(b, Toolbars_Status_Fetching); /* Since the new fetch is now official, update the current and previous */ /* page variables */ if (!b->ancestor) { /* Not speed critical, so avoid lots of nasty C-isms with malloc */ /* and so-on, by running through OS_CLI. */ _swix(OS_CLI, _IN(0), "Set Browse$PreviousPage <Browse$CurrentPage>"); _swix(OS_SetVarVal, _INR(0,4), "Browse$CurrentPage", b->urlfdata, strlen(b->urlfdata), 0, 4); } /* (Initialisation to an empty state is now complete, so we're */ /* ready to fetch a new page). */ } /* We're not waiting, but if there's also no data left to fetch, */ /* then we're just chugging through the list of tokens that the */ /* library is generating, telling various bits of the code about */ /* their contents (e.g. a new image, a new form). In this case, */ /* change the fetch status so the status bar can reflect the new */ /* situation. */ if (!remain) { b->fetch_status = BS_PROCESS; toolbars_update_status(b, Toolbars_Status_Processing); } /* If tptr is null, there are no HStream structures (see the */ /* html_get_next_token call). But we're not waiting either, */ /* so must be at the end of the file - stop the fetch. */ if (!tptr) { if (b->last_token->tag == TABLE && ISBODY(b->last_token)) { /* If the last thing the reformatter dealt with was a table, */ /* then extra table structures could have been added by */ /* HTMLLib and the reformatter may only have partially */ /* finished laying the table out. So kick off a reformat */ /* for that table to ensure it is up to date. */ /* */ /* Of course, it is important to ensure that any tokens that */ /* were added to the token stream are preprocessed before */ /* starting a reformat. */ fetch_preprocess_token(b, b->last_token); reformat_format_from(b, b->cell->nlines - 2, 1, -1); } #ifdef TRACE if (tl & (1u<<6)) Printf("\nfetch_fetcher: Finished, so stopping and exiting.\n"); #endif fetch_stop(b, 1); return; } else fetch_preprocess_token(b, tptr); /* Closure of series of ifs that checked the state of 'waiting' */ /* amongst other things, to handle redirections etc. The bulk */ /* of the code deals with a conventional fetch. */ } /* Closure of for loop that deals with several fetches per null */ } /* If start is >= 0, there is data that can be used for */ /* displaying the page; so start a reformat based on that */ /* data. Start from 'one line up' as the last line may have */ /* been only partially finished when it was last redrawn. */ if (start >= 0) reformat_format_from(b, start - 1, 0, -1); } /*************************************************/ /* fetch_chkerror() */ /* */ /* Called by low level fetch routines instead of */ /* the ChkError macro, as it stops the current */ /* fetch correctly before reporting the error. */ /* */ /* Parameters: Pointer to a browser_data */ /* structure relevant to the fetch; */ /* Pointer to a _kernel_oserror */ /* structure, which contains the */ /* error to report (or NULL). */ /* */ /* Returns: 0 if there was no error, else 1. */ /*************************************************/ int fetch_chkerror(browser_data * b, _kernel_oserror * e) { if (e) { /* There is an error - cancel the fetch */ fetch_cancel(b); /* Report the error */ show_error_ret(e); /* Flag the error in the returned value */ return 1; } return 0; } /*************************************************/ /* fetch_cancel() */ /* */ /* Aborts a fetch, closing any relevant streams, */ /* freeing up any claimed memory that was only */ /* relevant to the fetch, but leaves the page */ /* fetched so far visible. */ /* */ /* Parameters: A pointer to the browser_data */ /* structure relevant to the fetch */ /* to be cancelled. */ /*************************************************/ _kernel_oserror * fetch_cancel(browser_data * b) { /* If there is a fetch, and the HTML data isn't being used by the */ /* display routines, close the fetch handle and free up any memory */ /* associated with it. */ if ((b->fetch_handle) && ((b->fetch_handle) != (b->display_handle))) html_close(b->fetch_handle); b->fetch_handle = 0; /* If a META tag is about to do a reload, cancel this */ if (b->meta_refresh_at) deregister_null_claimant(Wimp_ENull, (WimpEventHandler *) meta_check_refresh, b); b->meta_refresh_at = 0; /* If not fetching, exit here */ if (!fetch_fetching(b)) return NULL; /* Stop everything else */ fetch_stop(b, 1); /* Ensure the page is correctly formatted */ if (b->cell->nlines) reformat_format_from(b, b->cell->nlines - 1, 1, -1); return(NULL); } /*************************************************/ /* fetch_stop() */ /* */ /* Stops a fetch, optionally discarding the */ /* HTML source, making sure the browser window */ /* state (buttons, status bar animation etc.) is */ /* correct, any open files are closed, and so */ /* forth. In the UI sense this is higher level */ /* than fetch_cancel, though fetch_cancel calls */ /* this as part of doing other cancel actions, */ /* and is therefore the higher level function. */ /* */ /* Parameters: A pointer to the browser_data */ /* structure relevant to the fetch */ /* to be stopped; */ /* 1 to keep the HTML source, 0 to */ /* destroy it. */ /*************************************************/ void fetch_stop(browser_data * b, int keep_source) { /* Destroy the source, provided the browser was fetching any */ if (fetch_fetching(b) && !keep_source) browser_destroy_source(b); /* Set the fetch status to idle */ b->fetch_status = BS_IDLE; /* If data was being saved to a file, close that file */ if (b->savefile) { fclose(b->savefile); b->savefile = NULL; } /* The savelink flag tells the browser to save the next fetch as data, */ /* even if it is parsable. Want to make sure that flag is clear now to */ /* avoid complications later on. */ b->savelink = 0; /* If there is a fetch, and the associated HTML document isn't being */ /* used by the display routines, close that fetch handle and free */ /* any memory associated with it. */ if ((b->fetch_handle) && (b->fetch_handle != b->display_handle)) html_close(b->fetch_handle); b->fetch_handle = 0; /* Discard the URL being fetched */ #ifdef TRACE if (tl & (1u<<12)) Printf("fetch_stop: Chunk CK_FURL set to 0\n"); #endif memory_set_chunk_size(b, NULL, CK_FURL, 0); b->reloading = 0; toolbars_cancel_status(b, Toolbars_Status_Fetching); /* Check that the window extent is large enough to fit the whole page in */ reformat_check_extent(b); /* Set up the window buttons */ toolbars_set_button_states(b); } /*************************************************/ /* fetch_authorisation_proceed() */ /* */ /* Given a browser_data structure with a URL */ /* containing a host and a pointer to a realm */ /* string, proceed with an authorisation */ /* request based on the data in the global */ /* 'authorise' flex block (handled by the */ /* functions in Authorise.c). */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the authorisation */ /* request; */ /* */ /* Pointer to a urlstat structure */ /* giving the fetch context, or NULL */ /* to get it from the browser_data */ /* structure's fetch_handle field */ /* (no good for images, obviously); */ /* */ /* Pointer to a string containing */ /* the realm for the request; */ /* */ /* Pointer to the request URL. */ /*************************************************/ void fetch_authorisation_proceed(browser_data * b, urlstat * context, char * realm, char * url) { int ok, l, s, offset; char host [128]; char base64 [(MaxAuthUser + MaxAuthPass + 2) * 4 / 3]; char authcode[(MaxAuthUser + MaxAuthPass + 2)]; urlstat * up; _kernel_oserror * e; /* Clear the 'authorising' flag */ authorising = 0; if (!context) { /* set 'up' to point to the head of the urlstat structure linked list */ up = fetch_list; /* It's another linked list traversal... As long as we aren't at the */ /* end of the list, and we haven't reached the item relating to this */ /* fetch, keep looking. */ while (up && up->session != b->fetch_handle) up = up->next; /* After the above loop, 'up' points to the structure for this */ /* fetch or is null; in the latter case, give an error. */ if (!up) { fetch_cancel(b); erb.errnum = Utils_Error_Custom_Normal; /* Nasty error but can recover from it here */ StrNCpy0(erb.errmess, lookup_token("StrNotFd:Internal error: Can't find structure in %0.", 0, "fetch_authorisation_proceed()")); show_error_ret(&erb); return; } } else up = context; /* Mark this fetch as authorised once already - if the server */ /* resends an authorisation request the fetcher will know */ /* that the authorisation failed (see html_get_next_token). */ up->authorised = 2; if (up->extradata) s = flex_size((flex_ptr) &up->extradata); else s = 0; /* Work out the host name */ urlutils_host_name_from_url(url, host, sizeof(host)); /* Store the details in the authcode block */ offset = authorise_find_user_name(host, realm); if (offset < 0) { fetch_authorisation_fail(b); return; } strcpy(authcode, authorise + offset); strcat(authcode, ":"); offset = authorise_find_password(host, realm); if (offset < 0) { fetch_authorisation_fail(b); return; } strcat(authcode, authorise + offset); /* Encode the block */ l = encode_base64(authcode, strlen(authcode), base64); base64[l] = 0; /* Allocate memory for the encoded data as a */ /* header entry. */ /* */ /* +2 accounts for CR + LF termination. */ l += strlen(AuthorisationStr) + 2; if (s) ok = flex_extend((flex_ptr) &up->extradata, s + l); else ok = flex_alloc((flex_ptr) &up->extradata, l + 1); if (!ok) { fetch_cancel(b); make_no_fetch_memory_error(12); show_error_ret(&erb); return; } if (s) memmove(up->extradata + l, up->extradata, s); /* Copy the data in */ strcpy(up->extradata, AuthorisationStr); strncpy(up->extradata + strlen(AuthorisationStr), base64, l - 23); up->extradata[l - 2] = '\r'; up->extradata[l - 1] = '\n'; if (!s) up->extradata[l] = 0; /* Restart the fetch with authentication */ e = url_get_url(0, up->session, up->method, url, up->extradata, NULL, 2); if (e) { fetch_cancel(b); show_error_ret(e); } return; } /*************************************************/ /* fetch_authorisation_fail() */ /* */ /* Called when authorisation for a URL fails in */ /* some way. Reports an appropriate error and */ /* stops the fetch. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the fetch. */ /*************************************************/ void fetch_authorisation_fail(browser_data * b) { /* Cancel the fetch */ authorising = 0; fetch_cancel(b); /* Give the error */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("BadAuthor:Authorisation failed; you must use a valid user name and password.", 0, 0)); show_error_ret(&erb); } /*************************************************/ /* html_get() */ /* */ /* Fetches and optionally starts parsing HTML. */ /* */ /* Parameters: Pointer to URL to fetch; */ /* Pointer to extra data for POST */ /* etc.; */ /* Pointer to an int into which a */ /* handle for this fetch will be */ /* placed; */ /* The fetch method, e.g. POST or */ /* GET; */ /* Pointer to the user name for */ /* MailServ (if in a multiuser */ /* environment); */ /* 1 to allow parsing, else 0; */ /* 1 to allow proxying, else 0. */ /* */ /* Returns: See parameters list. */ /*************************************************/ _kernel_oserror * html_get(char * url, char * extradata, int * handle, int method, char * user, int allowparse, int proxy) { _kernel_oserror * e; int ok; unsigned int h; #ifdef TRACE if (tl & (1u<<6)) Printf("html_get: Called\n"); #endif *handle = 0; /* Register the session with the URL module */ e = url_register(0, &h); // Sort out the proxying code properly!... /* Deal with proxying if necessary */ if (!e && choices.use_proxy) { char method[64]; char * method_ptr; int method_len; /* Extract the fetch method from the proxy address */ method_ptr = strstr(lookup_choice("ProxyAddress:http://127.0.0.1/", 0, 0), ":"); if (method_ptr) { method_len = (int) method_ptr - (int) tokens + 1; if (method_len > sizeof(method) - 1) method_len = sizeof(method) - 1; strncpy(method, tokens, method_len); method[method_len] = 0; } else strncpy(method, "http:", sizeof(method)); e = url_set_proxy(0, h, tokens, method, 0); } if (!e) { urlstat * up = NULL; #ifdef TRACE if (tl & (1u<<6)) Printf("html_get: Session registered, ID is %d\n",h); #endif /* Allocate memory for the fetch */ #ifdef TRACE if (tl & (1u<<12)) Printf("html_get: malloc %d for 'urlstat' structure\n",sizeof(urlstat)); #endif up = malloc(sizeof(urlstat)); /* If the allocation failed, report the problem */ if (!up) { url_deregister(0,h); make_no_fetch_memory_error(1); return &erb; } #ifdef TRACE malloccount += sizeof(urlstat); if (tl & (1u<<13)) Printf("** malloccount: %d\n",malloccount); #endif /* The allocation succeeded; fill the claimed memory with zeros */ /* and initialise various other parts of the block */ memset(up, 0, sizeof(urlstat)); up->session = * handle = (int) h; /* The fetch's session handle */ up->type = TYPE_HTMLFILE; /* Type of file - state it is an HTML file for now */ up->fetching = 1; /* We are still fetching */ up->next = fetch_list; /* Point to the top of the list in the Next entry */ up->method = method; /* Current fetch method */ up->extradata = NULL; /* Filled in later, if there is extra data */ up->allowparse = allowparse; /* Do we parse the data? */ fetch_list = up; /* If there is any extra data for POST or whatever, deal with it. */ /* The POST request entries must come first in the extra header */ /* info, so that the browser can make the assumption that */ /* everything from Content-Type forwards may be stripped in the */ /* event of a redirection when the current fetch method is POST. */ if (extradata) { int len; len = strlen(extradata); /* Allocate space for the extra data, the anchor stored in up->extradata */ #ifdef TRACE if (tl & (1u<<12)) Printf("html_get: flex_alloc %d for 'extradata' store\n",len + 3); #endif if (!flex_alloc((flex_ptr) &up->extradata, len + 3)) { url_deregister(0,h); make_no_fetch_memory_error(2); return &erb; } else { char head[50]; #ifdef TRACE flexcount += (len + 3); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif /* CR+LF into the top of the new block of memory */ up->extradata[0] = '\r'; up->extradata[1] = '\n'; /* Copy the extra data under the CR+LF */ strcpy(up->extradata + 2, extradata); /* Header entry for the extra data - again, the removal routines in the */ /* fetcher's redirection code assume that this comes before Content-Type */ /* and the actual body content to make life easy there. */ sprintf(head, "Content-Length: %d", len); /* Insert the header entries above the extra data already in the block. */ ok = html_insert_header(head, (flex_ptr) &up->extradata); /* (html_insert_header() returns 1 for success, 0 for memory claim failure) */ if (!ok) { url_deregister(0, h); make_no_fetch_memory_error(3); return &erb; } StrNCpy0(head, "Content-Type: application/x-www-form-urlencoded"); ok = html_insert_header(head, (flex_ptr) &up->extradata); if (!ok) { url_deregister(0, h); make_no_fetch_memory_error(4); return &erb; } } } /* If user details are given, insert the appopriate header entry */ if (user) { char head[50]; sprintf(head, "Mailserv-User: %s", user); ok = html_insert_header(head, (flex_ptr) &up->extradata); if (!ok) { url_deregister(0, h); make_no_fetch_memory_error(5); return &erb; } } /* If we aren't to use a proxy, say so in the header */ if (!proxy) { ok = html_insert_header("X-NoProxy:", (flex_ptr) &up->extradata); if (!ok) { url_deregister(0, h); make_no_fetch_memory_error(6); return &erb; } } /* Last but not least, do, er, something... */ { char c = 0; char * p = NULL; /* If non-zero on exit, p will point to the position of a hash */ /* in the URL (i.e., this finds out if an anchor is specified) */ p = fetch_find_name_tag(url); /* If there is a hash, turn it into a zero for now so the string */ /* contains just the URL and not the anchor. */ if (p) c = *p, *p = 0; e = url_get_url(0, /* Flags - must be 0, currently */ h, /* Session handle */ method, /* Fetch method */ url, /* URL to get */ up->extradata, /* Any extra data for POST etc. */ NULL, /* (Would be a status word) */ 2); /* Mode; 2 = header and data */ /* Put the hash back if was removed earlier. */ if (p) *p = c; } } #ifdef TRACE if (tl & (1u<<6)) { if (!e) Printf("html_get: Successful\n"); else Printf("html_get: Exitting with an error\n"); } #endif return e; } /*************************************************/ /* html_insert_header() */ /* */ /* Inserts a string into the header for an HTML */ /* fetch (for POST). Puts it at the top. */ /* */ /* Parameters: Pointer to the null terminated */ /* string to insert (this ends up */ /* CR+LF terminated in the header); */ /* Pointer to a flex anchor, which */ /* points to existing header data or */ /* is NULL if there is no header at */ /* the time of the function call. */ /* */ /* Returns: 1 if successful, or 0; you must */ /* externally generate an error */ /* appropriate to the memory claim */ /* having failed. */ /*************************************************/ int html_insert_header(char * header, flex_ptr data) { if (header) { int ok, s, len; len = strlen(header) + 2; /* 'data' points to an anchor; if this isn't null, find the */ /* size of the block the anchor points to */ if (*data) s = flex_size(data); else s = 0; /* If the block is > 0 bytes, extend it to a block big */ /* enough to hold the extra header data, else allocate a */ /* new block to hold it. Note that s will be zero if a new */ /* block was allocated, else it holds the old block size. */ #ifdef TRACE if (tl & (1u<<12)) { if (s) Printf("html_insert_header: flex_extend to %d for header store\n",len + s); else Printf("html_insert_header: flex_alloc %d for header store\n",len + 1); } #endif if (s) ok = flex_extend(data, len + s); else ok = flex_alloc(data, len + 1); /* Note len *plus 1*. */ if (!ok) return 0; #ifdef TRACE if (s) flexcount += len; else flexcount += (len + 1); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif /* Shuffle the header data down to make room for the new */ /* stuff at the top, if there was any data there to move. */ #ifdef TRACE if (tl & (1u<<18)) Printf("\0213html_insert_header: memove from %p to %p for %d bytes\0217\n",((int) (*data)) + len, *data, s); #endif if (s) memmove((void *) (((int) (*data)) + len), *data, s); /* Copy the new data into the top of the header. Don't want */ /* to overflow so use strncpy for extra caution... */ strncpy(*data, header, len - 2); /* Terminate the string with CR+LF */ ((char *) (*data))[len - 2] = '\r'; ((char *) (*data))[len - 1] = '\n'; /* If s is zero, i.e. a new block was created here, make */ /* sure it ends in zero (so C will think the string has */ /* ended properly if a string is read from the buffer). We */ /* can reference (array)[len] as the block allocation was */ /* done to len plus 1 bytes (see above). */ if (!s) ((char *) (*data))[len] = 0; } return 1; } /*************************************************/ /* html_close() */ /* */ /* Closes the specified handle, aborting any */ /* fetch and freeing up memory relating to it. */ /* */ /* Parameters: A fetch handle (usually from the */ /* browser_data->fetch_handle */ /* field). */ /*************************************************/ _kernel_oserror * html_close(unsigned int handle) { urlstat * up; urlstat ** pup; #ifdef TRACE if (tl & (1u<<6)) Printf("html_close: Called\n"); #endif url_deregister(0, handle); /* set 'pup' to point to the pointer to the head of the urlstat structure linked list */ pup = &fetch_list; /* set 'up' to point to the head of the urlstat structure linked list */ up = *pup; /* It's another linked list traversal... As long as we aren't at the */ /* end of the list, and we haven't reached the item relating to this */ /* fetch, keep looking. */ while ((up) && (up->session != handle)) { pup = &(up->next); up = up->next; } /* After the above loop, 'up' points to the structure for this */ /* fetch or is null; in the latter case, give an error. */ if (!up) { erb.errnum = Utils_Error_Custom_Fatal; StrNCpy0(erb.errmess, lookup_token("StrNotFd:Internal error: Can't find structure in %0.", 0, "html_close()")); #ifdef TRACE if (tl & (1u<<6)) Printf("html_close: Exiting with error\n"); #endif return &erb; } /* Make the pointer pointing to this structure point to the next one */ *pup = up->next; /* Free blocks associated with the urlstat structure */ #ifdef TRACE if (up->stream) { if (tl & (1u<<12)) Printf("html_close: Calling HtmlStreamFree on %p\n",up->stream); if (tl & (1u<<18)) Printf("\0212Closing stream %p\0217\n",up->stream); } #endif if (up->stream) { unsigned int context = HtmlReturnContext(up->stream); browser_data * browser = last_browser; /* Should Never Happen...! */ if (!context) { erb.errnum = Utils_Error_Custom_Fatal; StrNCpy0(erb.errmess, lookup_token("NoContxt:Serious internal error - Block is already free or was not HtmlAlloc'd in html_close(); must exit immediately.", 0, 0)); return &erb; } /* Ensure that any HStream pointers inside any current browser_data */ /* structures are not part of this stream - if so, clear them. */ while (browser) { /* For now, just a few selected items */ if (browser->selected && HtmlReturnContext(browser->selected) == context) { browser_clear_selection(browser, 0); browser->selected = NULL; } if (browser->highlight && HtmlReturnContext(browser->highlight) == context) { browser_clear_highlight(browser, 0); browser->highlight = NULL; } if (browser->pointer_over && HtmlReturnContext(browser->pointer_over) == context) browser->pointer_over = NULL; browser = browser->previous; } HtmlStreamFree(up->stream); } if (up->context) { #ifdef TRACE if (tl & (1u<<12)) Printf("html_close: free block %p for 'context' field of 'urlstat' structure\n",up->context); #endif free(up->context); up->context = NULL; } #ifdef TRACE if (up->extradata) { if (tl & (1u<<12)) Printf("html_close: flex_free block %p for 'extradata' field of 'urlstat' structure\n",&up->extradata); flexcount -= flex_size((flex_ptr) &up->extradata); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); } #endif if (up->extradata) flex_free((flex_ptr) &up->extradata); /* Finally, get rid of the structure itself */ #ifdef TRACE if (tl & (1u<<12)) Printf("html_close: free block %p holding 'urlstat' structure\n",up); malloccount -= sizeof(urlstat); if (tl & (1u<<13)) Printf("** malloccount: %d\n",malloccount); #endif free(up); #ifdef TRACE if (tl & (1u<<6)) Printf("html_close: Successful\n"); #endif return NULL; } /*************************************************/ /* html_get_next_token() */ /* */ /* Gets a chunk of document source from a given */ /* fetch handle, and may generate new HStream */ /* structures as the document is passed over to */ /* the HTML library parser. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the fetch or NULL; */ /* */ /* The fetch handle; */ /* */ /* Pointer to int into which the */ /* number of bytes still to be */ /* fetched is played; */ /* */ /* Pointer to int into which the */ /* number of bytes fetched so far is */ /* placed; */ /* */ /* Pointer to an HStream *, into */ /* which the address of the base of */ /* the token list is written, or */ /* NULL to signal 'not ready'; */ /* */ /* Pointer to an int, into which a */ /* reason code is placed: */ /* */ /* 0: Token has been received OK, */ /* 1: We are waiting for something, */ /* 2: A redirect has been detected */ /* (in this case, *remaining will */ /* point at the new URL), */ /* 3: This data is not parseable (in */ /* this case, *remaining holds a */ /* filetype); */ /* */ /* Pointer to pointer to the store */ /* for the whole of the data fetched */ /* so far (if any), be it an HTML */ /* document, image, or whatever; */ /* */ /* Pointer to string holding the URL */ /* that is being fetched; */ /* */ /* 1 if this is an image fetch, else */ /* 0 for HTML or unknown. */ /* */ /* Returns: See parameters list. */ /* */ /* Assumes: That if the browser_data struct */ /* pointer is NULL, the fetch is not */ /* for an internal URL. The other */ /* pointers must NOT be NULL unless */ /* it is specifically stated that */ /* they may be in the parameters */ /* list. */ /*************************************************/ _kernel_oserror * html_get_next_token(browser_data * b, unsigned int handle, int * remaining, int * size, HStream ** token, int * waiting, flex_ptr source, char * url, int image) { _kernel_oserror * e = NULL; int r = 0; char ref_url[2048]; urlstat * up; #ifdef TRACE if (tl & (1u<<6)) Printf("html_get_next_token: Called\n"); #endif /* Start in the default state of having no HStream to pass back */ /* through *token. */ if (token) *token = NULL; /* Until we know better, signal that we're waiting */ *waiting = 1; /* Ensure a fetch buffer is allocated */ if (!fetch_buffer) { fetch_buffer = malloc(FetchBufferSize); /* See top of this file */ if (!fetch_buffer) { make_no_cont_memory_error(8); #ifdef TRACE if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n"); #endif return &erb; } } /* up points to the first of the urlstat structure linked list; */ /* get a pointer to the one that the fetch handle refers to. */ up = fetch_list; while((up) && (up->session != handle)) up = up->next; if (!up) { erb.errnum = Utils_Error_Custom_Fatal; StrNCpy0(erb.errmess, lookup_token("StrNotFd:Internal error: Can't find structure in %0.", 0, "html_get_next_token()")); #ifdef TRACE if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n"); #endif return &erb; } /* Only look for an anchor and use url_read_data for URLs which */ /* are not internal. */ StrNCpy0(ref_url, url); if (image || b->displayed == Display_Fetched_Page) { int status = 0; /* Want to make sure we work on a URL which doesn't */ /* have an anchor in it, so copy over the url to */ /* a local buffer and if there's a '#' marking an */ /* anchor, replace it with a string terminator. */ char * p = fetch_find_name_tag(ref_url); if (p) * p = 0; /* If there isn't an authorisation request in progress, and the */ /* fetch is apparently in progress, and the authorisation status */ /* isn't '1' (which means 'doing'), get some data from the URL */ /* module. The url_read_data call puts the number of bytes read */ /* into r. */ if (!authorising && up->fetching && (up->authorised != 1)) { e = url_read_data(0, /* Flags - must be 0 at present */ handle, /* Session handle */ fetch_buffer, /* Buffer to receive data */ FetchBufferSize, /* The buffer's size */ &status, /* Protocol status */ &r, /* Number of bytes read */ remaining); /* Number of bytes left to get */ /* Deal with cookies */ if (!e && (status & (1u<<16))) e = cookies_process_cookie(b); } } else { /* This is an internal URL, so treat specially */ int ok; char * extra = ""; char * tail = ""; int len, exoff, toff; if (*source) flex_free(source); /* Look up the token embedded in the URL */ lookup_token(url + Int_URL_Len, 1, 0); /* Find a ':' separating extra information and point just past it */ exoff = urlutils_internal_extra(url); if (exoff) extra = url + exoff; /* Work out the length that the HTML file we're about to generate will be; */ /* this will be at least as long as the looked up token, plus a display */ /* type dependent extra amount. */ len = strlen(tokens) + 1; switch (b->displayed) { case Display_External_Image: { FILE * null; /* For external images, use of sprintf() precludes the use of */ /* general calculations to work out the string length. So, */ /* need to fprintf to NULL, find out how many bytes were */ /* written, and use this value instead... */ if (*extra) { toff = urlutils_internal_tail(url); if (toff) tail = url + toff; } null = fopen("Null:", "wb"); if (null) { len = fprintf(null, tokens, extra, extra, tail); fclose(null); } else len = -1; if (len < 0) { /* If the above fails, do our best to calculate the length. */ /* This will always overestimate the size (safer to do this */ /* than underestimate!). */ len = strlen(tokens) + 1; /* For external images, need to fit the extra data in twice, and */ /* try to find a filename separator for a picture caption (put */ /* this in 'tail'). */ if (*extra) len += strlen(extra) * 2 + strlen(tail) + 2; } } break; } /* Claim memory for the page; complain if this fails */ ok = flex_alloc(source, len); if (!ok) { make_no_cont_memory_error(1); #ifdef TRACE if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n"); #endif return &erb; } /* Construct the page in the claimed block */ switch (b->displayed) { case Display_External_Image: { memset(*source, 0, len); sprintf(*source, tokens, extra, extra, tail); } break; } /* Set up fetch flags to say that a fetch has been completed; since */ /* we've filled in the document source store here, say that zero */ /* bytes have been fetched (otherwise code below will try to copy */ /* data out of the fetch_buffer block). */ r = 0; remaining = 0; up->identified = 1; up->allowparse = 1; up->fetched = 1; up->fetching = 0; } /* If there isn't an error, and more than zero bytes have been read, */ /* deal with the data (if any) returned from the above call. */ if (r && !e) { int ok, oldsize; /* 'fetched' is a flag which if set indicates at least 1 byte has been */ /* got so far. If fetched is zero, and there is data in the source */ /* store (i.e. 'source' is not NULL) then free up the store as it does */ /* not hold any valid data (must be from an old fetch). */ if (!up->fetched && *source) { #ifdef TRACE if (tl & (1u<<12)) Printf("html_get_next_token: (1) flex_free block %p which held page source\n",source); flexcount -= flex_size(source); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(source); *source = NULL; } /* Signal that there's definitely data fetched now. */ up->fetched = 1; /* If there's store allocated at this point, it holds valid source; extend */ /* it by the number of bytes read from the url_read_data call. Else, alloc */ /* a new buffer to hold the data. */ #ifdef TRACE if (tl & (1u<<12)) { if (*source) Printf("html_get_next_token: flex_extend by %d to %d for page source store\n",r,flex_size(source) + r); else Printf("html_get_next_token: flex_alloc %d for page source store\n",r); } #endif if (*source) { oldsize = flex_size(source); ok = flex_extend(source, oldsize + r); } else { oldsize = 0; ok = flex_alloc(source, r); } #ifdef TRACE flexcount += r; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif if (size) *size = oldsize + r; /* Report an error if the allocation failed */ if (!ok) { make_no_cont_memory_error(1); #ifdef TRACE if (tl & (1u<<6)) Printf("html_get_next_token: Exiting with error\n"); #endif return &erb; } /* The data block has been created/extended successfully, so copy the */ /* data from the url_read_data call into it. */ #ifdef TRACE if (tl & (1u<<18)) Printf("\0216html_get_next_token: memcpy from %p to %p for %d bytes\0217\n",((char *) (*source)) + oldsize, fetch_buffer, r); #endif memcpy(((char *) (*source)) + oldsize, fetch_buffer, r); } /* If we're not authorising the transfer and data has been fetched... */ if (!authorising && up->fetched) { unsigned int hf = 0; HStream * new = NULL; /* If the stream has been identified as HTML... */ if (up->identified) { /* If there's no parsing context, get one by calling HtmlParse() */ /* - this initialises the HTML parser, getting it ready to parse */ /* a document (though it need not be present at this stage). */ /* */ /* First time round, this won't be called as the stream hasn't */ /* been identified with HtmlIdentify() yet. */ if (up->context == NULL) { up->context = HtmlParse(ref_url, /* Full page URL, so parser can handle relative links */ 0, /* Length of document - zero at present (not known) */ up->type, /* Return type from the HtmlIdentify call */ #ifdef FRAMES_SUPPORT 1); #else 0); #endif r = *source ? flex_size(source) : 0; } /* If there is new data in the source store (size = r) and no error at */ /* present, attempt to parse the chunk of data with HtmlGetStream. */ if (r && !e) { new = HtmlGetStream(up->context, /* Parser context, from HtmlParse() */ (char **) source, /* Pointer to start of the complete document */ r, /* Size of the chunk that has been added */ &hf); /* Flags from HTMLLib, e.g. 'have more data' */ up->stream = new; #ifdef TRACE if (tl & (1u<<18)) Printf("\0211(New stream for %p, %p)\0217\n", b, up->stream); #endif } if (!new) { /* There are no new HTML library structures */ if (up->lasttoken) { /* There is no new data, but lasttoken indicates there are more tokens */ /* left in the token stream from earlier calls that haven't been dealt */ /* with. So move to the next one. */ up->lasttoken = up->lasttoken->next; if (token) *token = up->lasttoken; } } else { /* There are some new HTML library structures. */ if (!(hf & HTML_GOT_MORE_IN_A_TABLE)) { /* The flag is unset, so the structures were added to the main token */ /* stream and not to part of a table structure. */ if (up->lasttoken) { /* Even though there are new structures, we still have older ones */ /* that are not dealt with, so move to the next one (the remote */ /* server is sending us data than we're processing it - yay!). */ up->lasttoken = up->lasttoken->next; if (token) *token = up->lasttoken; } else { /* There are no earlier structures left to deal with, so start on */ /* the first of the new batch. */ up->lasttoken = new; if (token) *token = up->lasttoken; } } else { /* The HTML_GOT_MORE_IN_A_TABLE flag is set, so structures were added to */ /* a table arrangement, as well as (possibly) the main stream after it. */ if (!up->lasttoken) { /* We weren't waiting to process anything from an earlier call, so */ /* start on this new table structure. */ up->lasttoken = new; if (token) *token = up->lasttoken; } else { /* We have undealt with structures from a previous fetch. Now, if */ /* we are already on the same table structure as returned by the */ /* HtmlGetStream call, then stay there (i.e. process the new data */ /* inside the table). Otherwise, move on. */ if (up->lasttoken != new) up->lasttoken = up->lasttoken->next; if (token) *token = up->lasttoken; } } } /* If we've moved on to, or were already on no token, then whether or */ /* not the fetch is still in progress determines whether or not we're */ /* waiting. Otherwise, we aren't waiting for anything. */ if (!up->lasttoken) *waiting = !!up->fetching; else *waiting = 0; } else if (up->authorised != 1) { /* The stream hasn't been identified as HTML, text or whatever, */ /* but there isn't an authorisation in progress. */ int s, o = 0; char * redirect; int code; int type; int parseable; /* Get the fetch status */ if (image || b->displayed == Display_Fetched_Page) { e = url_status(0, handle, &s, NULL, NULL); if (e) return e; } else s = URL_Status_Done; redirect = NULL; type = TYPE_UNKNOWN; parseable = 0; /* HttpStripHeaders, when passed a pointer to some document data, */ /* and an offest into that stream, returns the offset into the */ /* stream at which it starts assuming HTTP style headers (if */ /* there is such a point). */ o = *source ? HttpStripHeaders((char *) *source, flex_size(source)) : 0; /* If o is 0, there were no HTTP headers. If o is -1, there wasn't */ /* enough data to tell. Else, there were headers, and o is the */ /* offset into the stream of the data that follows those headers. */ if (o > 0) { #ifdef DUMP_HEADERS { FILE * file; int byte; file = fopen("<Wimp$ScrapDir>.Headers", "ab"); if (!image) fprintf(file, "For URL '%s', received header:\r\n\r\n", url); else fprintf(file, "For image '%s', received header:\r\n\r\n", url); for (byte = 0; byte < o; byte++) { fputc((int) (*((char *) (((int) *source) + byte))), file); } fclose(file); } #endif /* There are HTTP style headers in the data */ /* stream; try to identify that stream. */ code = HtmlIdentify(ref_url, /* Allow relative redirections to work */ (char *) *source, /* Pointer to head of data stream */ flex_size(source), /* Amount of data in the stream */ (s & URL_Status_Done) != 0, /* Is it a complete stream? 1 = yes */ &redirect, /* Will point to a URL if code = 302 */ &type, /* Will hold a filetype */ &parseable); /* Will say if the data is parseable */ /* Discard the stuff before the HTTP style headers by moving the data over them */ if (o != flex_size(source)) { #ifdef TRACE if (tl & (1u<<18)) Printf("\0213html_get_next_token: memove from %p to %p for %d bytes\0217\n", *source, (char*) (((int) *source) + o), flex_size(source) - o); #endif memmove(*source, (char*) (((int) *source) + o), flex_size(source) - o); /* Set o to the size of the data stream that is now in use, */ /* and shrink the source store to this size. */ #ifdef TRACE if (tl & (1u<<12)) Printf("html_get_next_token: flex_extend to shrink source code store by %d to %d\n",o,flex_size(source) - o); flexcount -= o; if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif } o = flex_size(source) - o; /* If the size of the store minus o is less than 0, HttpStripHeaders */ /* has failed completely and we must get out before everything else */ /* comes down...! */ if (o < 0) { erb.errnum = Utils_Error_Custom_Fatal; StrNCpy0(erb.errmess, lookup_token("HSHOvrrn:Serious internal error - HtmlStripHeaders has failed; must exit immediately.", 0, 0)); show_error_cont(&erb); /* This will cause exit(EXIT_FAILURE) eventually. */ } flex_extend(source, o); /* (Which shrinks the source store) */ /* Interpret the codes returned by HtmlIdentify(). */ switch (code) { /* Redirect; 'redirect' is a pointer to a new URL. */ case 302: { /* Stop the current fetch and free the source store, */ /* remembering to invalidate the anchor pointing to it */ url_stop(0, handle); #ifdef TRACE if (tl & (1u<<12)) Printf("html_get_next_token: (2) flex_free block %p which held page source\n",source); flexcount -= flex_size(source); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(source); *source = NULL; /* Ensure POST requests are now cleared (we shouldn't continue */ /* POSTing to redirected URLs) */ if (up->method == URL_Method_http_POST) { /* When the headers are build, the POST data (starting with a Content-Length */ /* entry) are put in first and everything else is inserted above it. As the */ /* comments on this code say, this is organised so we can simplify things */ /* here and just chop off everything at Content-Length and below, rather */ /* than having to carefully remove the appropriate header lines and body.� */ if (up->extradata) { char * strip = strstr(up->extradata, "Content-Length"); if (strip) { int len; /* How much do we want to keep? */ len = strstr(up->extradata, "Content-Length") - up->extradata; /* Don't use 'strip' in case flex shifted when 'len' was stacked */ #ifdef TRACE { int rmv = flex_size((flex_ptr) &up->extradata) - len; flexcount -= rmv; if (tl & (1u<<13)) Printf("** flexcount: %d\n",flexcount); } #endif /* Resize the block */ flex_extend((flex_ptr) &up->extradata, len); } } /* Change the fetch method to GET */ up->method = URL_Method_http_GET; } /* Customer specific */ #ifdef CUSTOMER_SPECIAL if ( !strcmp(redirect,"http://www.customer.com/login.html") || !strcmp(redirect,"http://www.customer.com/index.html") ) { // Send out cookie... redirect = (char *) "http://www.customer.com/simple.html"; } #endif /* Set the fetch's urlstat structure to say that */ /* no data has been fetched */ up->fetched = 0; /* Start a fetch on the new URL */ e = url_get_url(0, /* Flags (must be 0) */ handle, /* Session handle */ up->method, /* Fetch method */ redirect, /* URL to get */ up->extradata, /* Extra data for POST etc. */ NULL, /* We're ignoring the returned status */ 2); /* Mode 2 = fetch both header and data */ /* Return any errors that url_get_url generated */ if (e) return e; /* This function returns the address of the new URL in */ /* 'remaining', flagging this with a waiting status of 2 */ /* - and yes, this is quite odd. */ *waiting = 2; *remaining = (int) redirect; /* Not redirect_to, as it may be freed now */ } break; /* Authorise; the server requested authorisation before it */ /* would deliver the page. */ case 401: { char * realm; char host[128]; char username[MaxAuthUser]; char password[MaxAuthUser]; int po; /* Try to find the host and realm */ urlutils_host_name_from_url(ref_url, host, sizeof(host)); /* (The realm will lie in the string pointed to by */ /* 'redirect', between two double quotes). */ realm = authorise_read_realm(redirect); /* Ditch any document data got so far, we don't need it now */ /* (it only contains e.g. header information). */ if (source) { flex_free((flex_ptr) source); *source = NULL; } up->fetched = 0; /* If we've already tried this, then the authorisation failed, */ /* so give an appropriate error. */ if (up->authorised >= 2) { authorise_forget(host, realm); erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("BadAuthor:Authorisation failed; you must use a valid user name and password.", 0, 0)); return &erb; } /* Stop the URL module trying to get anything else, and set */ /* the flag to say we're authorising this fetch. */ url_stop(0, handle); up->authorised = 1; /* If there is a user name and / or password available already, */ /* use that and authenticate immediately. */ username[0] = password[0] = 0; po = authorise_find_user_name(host, realm); if (po >= 0) { StrNCpy0(username, authorise + po); po = authorise_find_password(host, realm); if (po >= 0) { StrNCpy0(password, authorise + po); } fetch_authorisation_proceed(b, up, realm, url); } /* Otherwise, get this information from a dialogue box and */ /* allow the authentication to happen later, when the user */ /* has done relevant things with the dialogue. */ else { char prompt[MaxAuthDisp]; int f; ObjectId dbox; /* Ensure the authorisation dialogue is created and event handlers */ /* are registered for it. */ e = authorise_create_dialogue((void *) b, &dbox); if (e) return e; /* -4 corrects for %s being replaced by host / realm strings, plus */ /* a terminator at the end of the whole lot. */ f = strlen(realm) + strlen(host); /* (But no terminators needed for these, so no '+ 1's) */ lookup_token("Authorise:Please enter a user name and a password for %%s at %%s",0,0); f += ((signed int) strlen(tokens)) - 4 + 1; /* Yuck... */ /* If the string is too big for the prompt or null, put a */ /* simple version in instead. */ if (f < 0 || f > sizeof(prompt)) { lookup_token("AuthorShr:Please enter a user name and a password.",0,0); e = button_set_value(0, dbox, AuthButton, tokens); } else { sprintf(prompt, tokens, realm, host); e = button_set_value(0, dbox, AuthButton, prompt); } if (e) return e; /* Empty the user name and password writables */ e = writablefield_set_value(0, dbox, AuthUserWrit, ""); if (e) return e; e = writablefield_set_value(0, dbox, AuthPassWrit, ""); if (e) return e; /* Show the dialogue and set the authorising flag */ e = toolbox_show_object(Toolbox_ShowObject_AsMenu, dbox, Toolbox_ShowObject_Centre, NULL, b->self_id, -1); if (e) return e; else menusrc = Menu_Authorise, authorising = 1; } } break; /* Catch anything else 'just in case'. Guess that the data */ /* is parseable and let this drop through to the ordinary */ /* URL handling code. */ default: parseable = 1; /* ...so no 'break' */ /* An ordinary URL. */ case 200: { if (!up->allowparse) parseable = 0; /* If the data is apparently parseable, flag the status as 'waiting' */ /* else flag it as 'not parseable'. */ switch (parseable) { default: case TYPE_UNKNOWN: case TYPE_IMAGEFILE: *waiting = 3; break; case TYPE_TEXTFILE: case TYPE_HTMLFILE: *waiting = 1; break; } if (parseable == TYPE_IMAGEFILE) { /* For images, stop the current fetch and 'redirect' to */ /* an internal page which will fetch the image inline. */ /* This is inefficient as you start to fetch the image */ /* twice; on slow servers, something of a killer... */ /* Unfortunately, pressure of time (yet *again*) */ /* precludes a more elegant solution for the moment. */ url_stop(0, handle); #ifdef TRACE if (tl & (1u<<12)) Printf("html_get_next_token: (2) flex_free block %p which held page source\n",source); flexcount -= flex_size(source); if (tl & (1u<<14)) Printf("** flexcount: %d\n",flexcount); #endif flex_free(source); *source = NULL; /* Set the fetch's urlstat structure to say that */ /* no data has been fetched */ up->fetched = 0; redirect = Internal_URL "PExtImage"; /* Start a fetch on the new URL */ e = url_get_url(0, /* Flags (must be 0) */ handle, /* Session handle */ up->method, /* Fetch method */ redirect, /* URL to get */ up->extradata, /* Extra data for POST etc. */ NULL, /* We're ignoring the returned status */ 2); /* Mode 2 = fetch both header and data */ if (e) return e; b->displayed = Display_External_Image; /* This function returns the address of the new URL in */ /* 'remaining', flagging this with a waiting status of 2 */ /* - and yes, this is quite odd. */ *waiting = 2; *remaining = (int) redirect; } else { *remaining = type; /* Set the type according to the parseable flag */ up->type = parseable; /* Flag that we've identified the stream */ up->identified = 1; } } break; /* If 0, haven't identified the stream yet */ case 0: break; /* Closure of 'switch' statement checking the return code */ /* from the HtmlIdentify() function call */ } /* Closure of 'if' statement that checked there was recognised data */ /* following HTTP style headers in the data stream */ } /* Closure of 'if' statement that checked the urlstat structure */ /* to see if authorisation was in progress (and only proceeded */ /* if it was not) */ } /* Closure of 'if' statement that followed the url_read_data */ /* function call and associated memory allocation procedures, */ /* and only proceeded if data had been fetched and authoristion */ /* was not flagged as being in progress. */ } /* If no data has been fetched from the url_read_data call */ /* from earlier on, and there hasn't been an error flagged */ /* so far, and both the general 'authorisation taking place' */ /* and urlstat-based 'authorisation in progress' flags are */ /* clear, ask the URL module for its current status. If it */ /* says it has finished (which would explain this set of */ /* circumstances - basically, having no data from the */ /* url_read_data call but nothing else is wrong), set the */ /* urlstat structure flag to say fetching is no longer in */ /* progress. */ if (!r && !e && up->authorised != 1 && !authorising) { int s; if (image || b->displayed == Display_Fetched_Page) { e = url_status(0, handle, &s, NULL, NULL); if (e) return e; } else s = URL_Status_Done; /* Internal URLs 'fetch' immediately */ if (s & URL_Status_Done) up->fetching = 0; } /* If we've been passed somewhere to put the size of the store, */ /* and if the store is present, return the size of it. */ if (size) *size = (*source) ? flex_size(source) : 0; #ifdef TRACE if (tl & (1u<<6)) { if (!e) Printf("html_get_next_token: Successful\n"); else Printf("html_get_next_token: Exitting with an error\n"); } #endif /* Exit, passing on any error if there is one */ return (e); } /*************************************************/ /* html_get_next_chunk() */ /* */ /* Gets a chunk of data from a stream, assuming */ /* that it is not HTML. */ /* */ /* Parameters: Pointer to a browser_data struct */ /* relevant to the fetch; */ /* */ /* The fetch handle; */ /* */ /* Pointer to buffer into which the */ /* fetched data will be placed (as */ /* a char *); */ /* */ /* Size of the buffer; */ /* */ /* Pointer to an int into which 1 is */ /* placed if the fetch is complete, */ /* else 0 is returned (this pointer */ /* may be NULL); */ /* */ /* Pointer to an int into which the */ /* number of bytes fetched is placed */ /* (which may also be NULL). */ /* */ /* Returns: See parameters list. */ /* */ /* Assumes: That if the browser_data struct */ /* pointer is NULL, the fetch is not */ /* for an internal URL. */ /*************************************************/ _kernel_oserror * html_get_next_chunk(browser_data * b, unsigned int handle, char * buffer, int size, int * done, int * bytes) { _kernel_oserror * e; urlstat * up; int s, t; // // This function does not know about internal URLs yet (so parameter 'b' is currently unused)... // BEWARE when using this, as of course this URL fetch may not be for page data under an // internal URL. // /* Find the urlstat structure for the fetch handle */ up = fetch_list; while (up && up->session != handle) up = up->next; if (!up) { erb.errnum = Utils_Error_Custom_Fatal; StrNCpy0(erb.errmess, lookup_token("StrNotFd:Internal error: Can't find structure in %0.", 0, "html_get_next_chunk()")); return &erb; } /* Read some data */ // Printf("handle, buffer, size, bytes: %p, %p, %d, %d\n",handle,buffer,size,bytes); e = url_read_data(0, handle, buffer, size, NULL, bytes, &t); if (e) return e; /* Get the fetch status */ e = url_status(0, handle, &s, NULL, NULL); if (e) return e; /* Fill in 'done' as appropriate to the fetch status and exit */ if (done) *done = (s & URL_Status_Done) ? 1 : 0; return NULL; } /*************************************************/ /* url_register() */ /* */ /* Registers a requirement to fetch a URL with */ /* the URL module. */ /* */ /* Parameters: Flags (must be 0 at present); */ /* Pointer to int into which the */ /* session handle is placed. May be */ /* NULL. */ /* */ /* Returns: See parameters list. */ /*************************************************/ _kernel_oserror * url_register(unsigned int flags, unsigned int * handle) { _kernel_oserror * e; unsigned int h; /* If there's a pointer to put the handle into, set it to zero initially */ if (handle) *handle = 0; e = _swix(URL_Register, _IN(0) | _OUT(1), flags, &h); /* If the call didn't return an error, store the session handle */ if ((!e) && (handle)) *handle = h; #ifdef TRACE if (tl & (1u<<6)) { if (!e) Printf("url_register: Registered ID %d\n",*handle); else Printf("url_register: Exitting with error\n"); } #endif return e; } /*************************************************/ /* url_deregister() */ /* */ /* Deregisters a requirement to fetch a URL with */ /* the URL module. */ /* */ /* Parameters: Flags (must be 0 at present); */ /* The session handle. */ /*************************************************/ _kernel_oserror * url_deregister(unsigned int flags, unsigned int handle) { #ifdef TRACE if (tl & (1u<<6)) Printf("url_deregister: Deregistering ID %d\n",handle); #endif /* Abort any current action */ url_stop(flags,handle); /* Deregister the session */ return _swix(URL_Deregister, _INR(0,1), flags, handle); } /*************************************************/ /* url_stop() */ /* */ /* Interrupts a fetch if one is going on. */ /* */ /* Parameters: Flags (must be 0 at present); */ /* The session handle. */ /*************************************************/ _kernel_oserror * url_stop(unsigned int flags, unsigned int handle) { #ifdef TRACE if (tl & (1u<<6)) Printf("url_stop: Stop with ID %d\n",handle); #endif return _swix(URL_Stop, _INR(0,1), flags, handle); } /*************************************************/ /* url_get_url() */ /* */ /* Starts fetching data from a URL. */ /* */ /* Parameters: Flags (must be 0 at present); */ /* The session handle; */ /* The fetch method, as in the */ /* html_get() function above; */ /* Pointer to URL to fetch; */ /* Pointer to any extra data to send */ /* for POST etc.; */ /* Pointer to an int into which a */ /* status flag is placed (this may */ /* be NULL); */ /* The fetch mode */ /* */ /* 0: Get data only, */ /* 1: Get header only, */ /* 2: Get both. */ /* */ /* Returns: See parameters list. */ /*************************************************/ _kernel_oserror * url_get_url(unsigned int flags, unsigned int handle, int method, char * url, char * extradata, unsigned int * status, int mode) { _kernel_oserror * e = NULL; int s; #ifdef TRACE if (tl & (1u<<6)) Printf("url_get_url: Called with ID %d\n",handle); #endif /* If a pointer to the int in which status information can be written */ /* is not NULL, set the current status to 0 */ if (status) *status = 0; if (url && !strncmp(url, Internal_URL, strlen(Internal_URL))) { /* For an internal URL, don't try to actually use the URL module... */ if (status) *status = 0; } else { #ifdef DUMP_HEADERS { FILE * file; file = fopen("<Wimp$ScrapDir>.Headers", "ab"); if (!extradata || (extradata && extradata[0] == 0)) fprintf(file, "Fetch URL '%s'; sending standard header\r\n\r\n", url); else { fprintf(file, "Fetch URL '%s'; sending standard header plus:\r\n\r\n%s\r\n", url, extradata); if (extradata[strlen(extradata) - 1] != '\n') fprintf(file, "\r\n"); } fclose(file); } #endif _swix(Hourglass_Start, _IN(0), 50); e = _swix(URL_GetURL, _INR(0,5) | _OUT(0), flags, handle, method, url, extradata, mode, &s); _swix(Hourglass_Off, 0); /* If the GetURL call didn't return an error, write the new status */ if (status && !e) *status = s; } #ifdef TRACE if (tl & (1u<<6)) { if (!e) Printf("url_get_url: Successful\n"); else Printf("url_get_url: Exitting with error\n"); } #endif return e; } /*************************************************/ /* url_read_data() */ /* */ /* Asks the URL module to copy some of the data */ /* it has fetched over into a buffer. */ /* */ /* Parameters: Flags (must be 0 at present); */ /* The session handle; */ /* Pointer to buffer into which the */ /* data is transferred, or NULL to */ /* just get a Pending state; */ /* The size of the buffer; */ /* Pointer to an int, into which a */ /* status word is placed; */ /* Pointer to int, into which the */ /* number of bytes read (and put in */ /* the bufrer) is placed; */ /* Pointer to int, into which the */ /* number of bytes that are still to */ /* be fetched s placed. */ /* */ /* Returns: See parameters list. */ /*************************************************/ _kernel_oserror * url_read_data(unsigned int flags, unsigned int handle, void * buffer, int size, int * status, int * read, int * pending) { _kernel_oserror * e; int s, r, p = 0; #ifdef TRACE if (tl & (1u<<6)) Printf("url_read_data: Called with ID %d\n",handle); #endif /* Ensure all returned data is set to a sensible default to start with */ if (status) * status = 0; if (read) * read = 0; if (pending) * pending = 0; /* Call the URL module */ e = _swix(URL_ReadData, _INR(0,3) | _OUT(0) | _OUTR(4,5), flags, handle, buffer, size, &s, &r, &p); #ifdef TRACE if (tl & (1u<<6)) Printf("url_read_data: Status %p, error %p returned\n",s,e); #endif /* In the absence of any errors, fill in the relevant returned data */ if (!e) { if (status) *status = s; if (read) *read = r; if (pending) *pending = p; } #ifdef TRACE if (tl & (1u<<6)) { if (!e) Printf("url_read_data: Successful\n"); else Printf("url_read_data: Exitting with error\n"); } #endif return e; } /*************************************************/ /* url_status() */ /* */ /* Returns the status of a fetch. */ /* */ /* Parameters: Flags (must be 0 at present); */ /* The session handle; */ /* Pointer to an int, into which a */ /* status word is placed; */ /* Pointer to an int, into which the */ /* server's response is placed; */ /* Pointer to an int, into which the */ /* number of bytes transferred so */ /* far is placed. */ /* */ /* Returns: See parameters list. */ /*************************************************/ _kernel_oserror * url_status(unsigned int flags, unsigned int handle, int * status, int * response, int * bytes) { _kernel_oserror * e; int s, r, b; #ifdef TRACE if (tl & (1u<<6)) Printf("url_status: Called with ID %d\n",handle); #endif /* Set returned data to zero to begin with */ if (status) *status = 0; if (response) *response = 0; if (bytes) *bytes = 0; e = _swix(URL_Status, _INR(0,1) | _OUT(0) | _OUTR(2,3), flags, handle, &s, &r, &b); /* In the absence of an error, fill in the returned data */ if (!e) { if (status) *status = s; if (response) *response = r; if (bytes) *bytes = b; } #ifdef TRACE if (tl & (1u<<6)) { if (!e) Printf("url_status: Successful\n"); else Printf("url_status: Exitting with error\n"); } #endif return e; } /*************************************************/ /* url_set_proxy() */ /* */ /* Instructs the URL module to fetch through a */ /* proxy (or not). */ /* */ /* Parameters: Flags (must be 0 at present); */ /* The session handle; */ /* Pointer to the base URL of the */ /* proxy server, e.g. for a local */ /* proxy, "http://127.0.0.1/"; */ /* Pointer to a string holding the */ /* protocol to use, e.g. "http:" or */ /* "ftp:"; */ /* 1 to disable proxying, 0 to */ /* enable it with the above data. */ /*************************************************/ _kernel_oserror * url_set_proxy(int flags, unsigned int session, char * baseurl, char * protocol, int noproxy) { return _swix(URL_SetProxy, _INR(0,4), flags, session, baseurl, protocol, noproxy); } /*************************************************/