/* Copyright 1998 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 : Multiuser.h */ /* */ /* Purpose: Functions and definitions to enable */ /* the browser to function in an */ /* Customer-style multiuser environment, */ /* if SINGLE_USER is undefined when the */ /* whole of the browser source is */ /* compiled. Otherwise, some functions */ /* available, but only those to do basic */ /* single user loading and saving of */ /* histories, hotlists etc. */ /* */ /* Author : Merlyn Kline for Customer browser */ /* This source adapted by A.D.Hodgkinson */ /* */ /* History: 23-Jul-97: Created. */ /* 16-Mar-98: Bulk of the code imported */ /* from Customer browser. */ /* Fair amount of rewriting / */ /* fixing required. */ /***************************************************/ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <time.h> #include "swis.h" #include "flex.h" #include "wimp.h" #include "wimplib.h" #include "event.h" #include "toolbox.h" #include "iconbar.h" #include "window.h" #include "gadgets.h" #include "svcprint.h" #include "Global.h" #include "Utils.h" #include "Browser.h" #include "ChoiceDefs.h" #include "Fetch.h" /* (Which itself includes URLstat.h) */ #include "History.h" #include "Hotlist.h" #include "ImgHistory.h" #include "Multiuser.h" #include "URLutils.h" #include "Multiuser.h" /* Local statics for all builds */ static ObjectId iconbar = NULL_ObjectId; #ifndef SINGLE_USER /* Local statics for multiuser builds */ static int pass_pos; static char current_pass [Limits_Multi_Pathname]; static char login_pb [Limits_Multi_Pathname]; static char trans_file [9]; /* Lowest common denominator maximum filename length (FileCore, 10 chars) minus 2, plus 1 for terminator */ static unsigned int login_stamp; static time_t login_time; static char login_password [Limits_Multi_Password]; static char login_user [Limits_Multi_UserName]; static char rnd_buf [Limits_Multi_Encoded]; static char rnd_buf1 [Limits_Multi_Encoded]; static ObjectId login; /* Static function prototypes for multiuser builds */ static int multiuser_login_ok (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int multiuser_login_cancel (int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle); static int multiuser_write_line (FILE * f, const char * s, int newline); static int multiuser_get_line (FILE * f, char * buf, int unknown); static unsigned multiuser_get_stamp (const char * f); static unsigned multiuser_get_length (const char * f); static int multiuser_file_exists (const char * f); static void multiuser_encode_line (char * buf, int keepnl, int magic); //static void multiuser_decode_data (char * data, int len, char * pass, int keepnl, int magic); static FILE * multiuser_open_postbox (const char * where, char * name); static _kernel_oserror * multiuser_read_user (const char * user, const char * password); static int multiuser_watch_flags (int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle); static int multiuser_check_login (int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle); #else /* Static function prototypes for single user builds */ static _kernel_oserror * multiuser_read_user (const char * name, const char * password); #endif /*************************************************/ /* multiuser_set_iconbar_variant() */ /* */ /* Set either the normal, greyed out or question */ /* mark icon bar icon variants. */ /* */ /* Parameters: Pointer to a string holding the */ /* suffix to the sprite name (so */ /* pointer to "n" or possibly a null */ /* string for the normal sprite, "g" */ /* for the greyed out icon, etc.). */ /* */ /* Assumes: The pointer may be NULL. */ /*************************************************/ _kernel_oserror * multiuser_set_iconbar_variant(const char * suffix) { const char * local_suffix = suffix ? suffix : ""; if ( iconbar != NULL_ObjectId || !toolbox_create_object(0, "Iconbar", &iconbar) ) { *lasttokn = 0; lookup_token("_SpriName",1,0); strcat(tokens, local_suffix); return iconbar_set_sprite(0, iconbar, tokens); } return NULL; } /*************************************************/ /* multiuser_login() */ /* */ /* Either read histories etc. based on the */ /* Choices in single user builds, or open the */ /* Log In dialogue box. */ /*************************************************/ _kernel_oserror * multiuser_login(void) { #ifdef SINGLE_USER return multiuser_read_user(NULL, NULL); #else int have_default = 0; static int tried_once = 0; int have_user = 0; int have_pass = 0; re_login = logged_in = 0; /* Show the 'greyed out' icon bar sprite */ show_error_ret(multiuser_set_iconbar_variant("g")); /* Create and show the Log In dialogue box */ RetError(toolbox_create_object(0, "LogIn", &login)); RetError(toolbox_show_object(0, login, Toolbox_ShowObject_Centre, NULL, NULL_ObjectId, NULL_ComponentId)); /* Fill in the user name and password writables */ have_default = strcmp(lookup_choice("DefaultUser:no",0,0), "no"); if (!have_default) { RetError(writablefield_set_value(0, login, LogInPassWrit, "")); RetError(writablefield_set_value(0, login, LogInUserWrit, "")); } else { RetError(writablefield_set_value(0, login, LogInPassWrit, lookup_choice("DefaultPass:",1,0))); if (*tokens) have_pass = 1; RetError(writablefield_set_value(0, login, LogInUserWrit, lookup_choice("DefaultName:",1,0))); if (*tokens) have_user = 1; } /* Fill in the message panel */ button_set_value(0, login, LogInPrompt, lookup_token("LogIn:To use the World Wide Web, please log in by supplying your user name and password.",0,0)); /* Event handlers */ RetError(event_register_toolbox_handler(login, ELogInOK, multiuser_login_ok, NULL)); RetError(event_register_toolbox_handler(login, ELogInCancel, multiuser_login_cancel, NULL)); /* Now the hacky bit; if default_user is set, call the OK handler */ /* directly... If everything works, the login dialogue will be */ /* closed before it is even seen, otherwise it will be opened */ /* with the default user name and password entered in it. */ if (!tried_once && have_default && have_user && have_pass) { tried_once = 1; multiuser_login_ok(0, NULL, NULL, NULL); } return NULL; #endif } /*************************************************/ /* multiuser_logout() */ /* */ /*************************************************/ _kernel_oserror * multiuser_logout(void) { #ifdef SINGLE_USER #ifndef REMOTE_HOTLIST /* Save the hotlist, if required */ #ifdef TRACE if (tl & (1u<<5)) Printf("multiuser_logout: Calling hotlist_save\n"); #endif if ( choices.save_hotlist != Choices_SaveHotlist_Never ) show_error_ret(hotlist_save(lookup_choice("HotlistSave:Browse:User.Hotlist",0,0))); #endif /* Same for the global history... */ if ( choices.save_history != Choices_SaveHistory_Never ) show_error_ret(history_save(lookup_choice("HistorySave:Browse:User.History",0,0))); /* ...and the image history */ if ( choices.save_image_history != Choices_SaveImageHistory_Never ) show_error_ret(imghistory_save(lookup_choice("ImageHistorySave:Browse:User.Images",0,0))); return NULL; #else _kernel_oserror * e = NULL; if (logged_in) { /* Save the hotlist, and discard it */ e = multiuser_save_hotlist(); hotlist_discard(); /* Throw away the histories */ history_limit(0); imghistory_limit(0); } return e; #endif } #ifndef SINGLE_USER /*************************************************/ /* multiuser_save_hotlist() */ /* */ /* Save the user's hotlist through the postbox */ /* scheme. */ /*************************************************/ _kernel_oserror * multiuser_save_hotlist(void) { FILE * f; char buf [Limits_Multi_Pathname]; char name[Limits_Multi_Pathname]; sprintf(buf, "%s.PostBoxes", choices.post_out); f = multiuser_open_postbox(buf, name); if (!f) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoPBoxes:No postboxes available at server. Wait a moment and try again.", 0, 0)); return &erb; } else { sprintf(buf, "X-Mercury-Command:SaveHotlist\n%s\n", user.name); /* Yuckery - we hope that the new naming scheme should */ /* prevent clashes. */ fclose(f); return hotlist_save_hotlist(name, buf, 0); } } /*************************************************/ /* multiuser_login_ok() */ /* */ /* Handle ELogInOK events (e.g. clicks on the */ /* 'OK' button in the Log In dialogue box). */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ static int multiuser_login_ok(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { _kernel_oserror * e; char user[Limits_Multi_UserName]; char pass[Limits_Multi_Password]; ChkError(writablefield_get_value(0, login, LogInPassWrit, pass, sizeof(pass), NULL)); ChkError(writablefield_get_value(0, login, LogInUserWrit, user, sizeof(user), NULL)); /* If multiuser_read_user returns an error, keep the login dialogue */ /* open. Otherwise, install two null handlers. One watches for the */ /* server's response to the login request. The other watches the */ /* re_login and logged_in flags and takes appropriate action when */ /* one or the other becomes set. */ e = multiuser_read_user(user, pass); if (!e) { register_null_claimant(Wimp_ENull, multiuser_check_login, NULL); register_null_claimant(Wimp_ENull, multiuser_watch_flags, NULL); /* Close the dialogue, too - don't want the user pressing 'OK' */ /* lots of times whilst waiting for the first login...! */ event_deregister_toolbox_handlers_for_object(login); toolbox_delete_object(0, login); login = NULL_ObjectId; } else show_error_ret(e); return 1; } /*************************************************/ /* multiuser_login_cancel() */ /* */ /* Handle ELogInCancel events (e.g. clicks on */ /* the 'Cancel' button in the Log In dialogue */ /* box). */ /* */ /* Parameters are as standard for a Toolbox */ /* event hander. */ /*************************************************/ static int multiuser_login_cancel(int eventcode, ToolboxEvent * event, IdBlock * idb, void * handle) { /* Didn't log in, so browser quits */ multiuser_logout(); exit(EXIT_SUCCESS); /* Stop compiler complaining about implicit returns in non-void functions... */ return 1; } /*************************************************/ /* multiuser_write_line() */ /* */ /*************************************************/ static int multiuser_write_line(FILE * f, const char * s, int newline) { fputs(s, f); if (newline) fputc('\n', f); return !ferror(f); } /*************************************************/ /* multiuser_get_line() */ /* */ /*************************************************/ static int multiuser_get_line(FILE * f, char * buf, int unknown) { char * p; /* Read up to 256 bytes into the buffer */ p = fgets(buf, 256, f); if (!p) return 0; /* Find either a newline or zero terminator, and ensure */ /* that this is treated as the end of the string by */ /* writing a zero over that byte. */ while (*p != '\n' && * p != '\0') p++; *p = '\0'; return 1; } /*************************************************/ /* multiuser_get_stamp() */ /* */ /*************************************************/ static unsigned multiuser_get_stamp(const char * f) { unsigned exec_addr; _swix(OS_File, _INR(0,1) | _OUT(3), 23, f, &exec_addr); return exec_addr; } /*************************************************/ /* multiuser_get_length() */ /* */ /*************************************************/ static unsigned multiuser_get_length(const char * f) { unsigned len; int type; _swix(OS_File, _INR(0,1) | _OUT(0) | _OUT(4), 23, f, &type, &len); if (!type) return 0; else return len; } /*************************************************/ /* multiuser_file_exists() */ /* */ /*************************************************/ int multiuser_file_exists(const char * f) { int type; _swix(OS_File, _INR(0,1) | _OUT(0), 23, f, &type); return (type != 0); } /*************************************************/ /* multiuser_encode_line() */ /* */ /*************************************************/ static void multiuser_encode_line(char * buf, int keepnl, int magic) { int i, len; char p; char * password = current_pass; /* Terminate the line with '\n' and a C string terminator for convenience */ len = strlen(buf); buf[len] = '\n'; buf[len + 1] = 0; /* Go through character by character */ for (i = 0; i <= len; i++) { p = password[pass_pos]; /* If we are to preserve newline data, don't EOR with the current */ /* character obtained from the password; similarly, ensure that */ /* we don't EOR if this would generate a newline character. If */ /* keeping newlines is unimportant, go ahead and EOR it anyway. */ if ( !keepnl || ( buf[i] != '\n' && (buf[i] ^ p) != '\n' ) ) { if ( buf[i] != p && buf[i] != 0 ) buf[i] ^= p; } /* Increment the position in the password */ pass_pos++; /* Have we reached a control character, i.e. the end of */ /* the password string? */ if (password[pass_pos] < 32) { unsigned int r1, r2, tmp; /* If the 'magic' flag is set, treat the password as */ /* a 64 bit binary number (stored as 2 32-bit nos. */ /* output by 'printf("%8x%8x")') and rotate it one */ /* bit to the right. */ if (magic) { sscanf(password, "%8x%8x", &r1, &r2); tmp = r2 & 1; r2 = r2 >> 1; if (r1 & 1) r2 |= (1u<<31); r1 = r1 >> 1; if (tmp) r1 |= (1u<<31); sprintf(password, "%08x%08x", r1, r2); } /* Point back to the first character of the password */ pass_pos = 0; } } } ///*************************************************/ ///* multiuser_decode_data() */ ///* */ ///*************************************************/ // //static void multiuser_decode_data(char * data, int len, char * pass, int keepnl, int magic) //{ // int i; // char * c; // char p; // char password[Limits_Multi_Pathname]; // // StrNCpy0(password, pass); // // c = data; // i = 0; // // while (c < data + len) // { // p = password[i]; // // /* Only EOR with the byte from the password if the encoder would have */ // /* EORed it - see multiuser_encode_line for the 'keep newline' info. */ // // if ( // !keepnl || // ( // *c != '\n' && // ((*c) ^ p) != '\n' // ) // ) // { // if ( // *c != 0 && // *c != p // ) // *c=(*c) ^ p; // } // // i++; // // /* Has the end of the password been reached? */ // // if (password[i] < 32) // { // unsigned int r1, r2, tmp; // // /* If 'magic' is set, rotate the password right one bit as */ // /* in multiuser_encode_line. */ // // if (magic) // { // sscanf(password, "%8x%8x", &r1, &r2); // // tmp = r2 & 1; // r2 = r2 >> 1; // // if (r1 & 1) r2 |= (1u<<31); // // r1 = r1 >> 1; // // if (tmp) r1 |= (1u<<31); // // sprintf(password, "%08x%08x", r1, r2); // } // // i = 0; // } // // c++; // } //} /*************************************************/ /* multiuser_create_unique_postbox_filename() */ /* */ /* Create a unique filename based on the machine */ /* IP address. This is an 8 character filename */ /* prefix stored in 'trans_file'. */ /* */ /* This name MUST NOT depend on the current */ /* settings of Post_In / Post_Out, as they may */ /* be invalid and/or could change after this */ /* function is called without it being called */ /* again. */ /*************************************************/ void multiuser_create_unique_postbox_filename(void) { char * tmp; /* If Inet$LocalAddr exists use it as the filename */ tmp = getenv("Inet$LocalAddr"); if (tmp) { StrNCpy0(trans_file, tmp); } /* Otherwise, seed the random number generator with */ /* the monotonic time and work from that. */ else { unsigned t; _swix(OS_ReadMonotonicTime, _OUT(0), &t); srand(t); sprintf(trans_file, "%x", rand()); } } /*************************************************/ /* multiuser_open_postbox() */ /* */ /*************************************************/ static FILE * multiuser_open_postbox(const char * where, char * name) { char buf[3]; int count = 0, tmp2; /* Assume buffer is Limits_Multi_Pathname long... Sigh... */ /* '+4' is one for the '.', one for the terminator, and two */ /* for the two digit suffix on trans_file. */ if (strlen(where) + strlen(trans_file) + 4 > Limits_Multi_Pathname) return NULL; /* Compile the full name in 'name' */ sprintf(name, "%s.%s", where, trans_file); tmp2 = strlen(name); /* Go through trying to find an unused file as */ /* 'nameXX', where XX is '00' to 'nn'. */ while(multiuser_file_exists(name)) { *(name + tmp2) = '\0'; sprintf(buf, "%02i", count++); strcat(name, buf); /* =8*! Ooooooh, yes. Shudder. */ if (count > 70) return NULL; } return fopen(name, "w"); } /*************************************************/ /* multiuser_read_user() (multiuser version) */ /* */ /*************************************************/ static _kernel_oserror * multiuser_read_user(const char * user, const char * password) { _kernel_oserror * e = NULL; char pass [Limits_Multi_Password]; char rnd_buf2 [Limits_Multi_Encoded]; char rnd_buf3 [Limits_Multi_Encoded]; char buf [Limits_Multi_Encoded]; unsigned int rnd, rnd1; FILE * f; char * c; /* Seed random number generator with current time */ srand(time(NULL)); /* Get two random numbers */ rnd = rand(); rnd1 = rand(); /* Write them as a string into rnd_buf as two hex numbers back */ /* to back, and copy this to rnd_buf2. */ sprintf(rnd_buf, "%08x%08x", rnd, rnd1); StrNCpy0(rnd_buf2, rnd_buf); /* Get another two random numbers, and again write them as two */ /* back to back hex numbers to rnd_buf1, copying to rnd_buf3. */ rnd = rand(); rnd1 = rand(); sprintf(rnd_buf1, "%08x%08x", rnd, rnd1); StrNCpy0(rnd_buf3, rnd_buf1); /* We must have a non-null user and non-null password string */ if (!user || !password || !*user || !*password) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoUser:You must enter a user name and password to use the World Wide Web.", 0, 0)); return &erb; } /* Take a local copy of the password (up to 11 chars plus terminator) */ StrNCpy0(pass, password); /* Create a postbox file */ sprintf(buf, "%s.PostBoxes", choices.post_out); f = multiuser_open_postbox(buf, login_pb); /* No postboxes available? Complain. */ if (!f) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoPBoxes:No postboxes available at server. Wait a moment and try again.", 0, 0)); return &erb; } /* Convert password to lower case */ for (c = pass; *c; c++) *c = tolower(*c); /* Set the global pointer into the password to the first */ /* character, and take a copy of the password in the */ /* global password buffer. */ pass_pos = 0; StrNCpy0(current_pass, pass); /* Encode the random data in rnd_buf2 with the current */ /* password, remembering not to scramble it by setting */ /* the 'magic' flag. */ multiuser_encode_line(rnd_buf2, 1, 0); /* Reset the password position, and now use the rnd_buf */ /* contents (same as rnd_buf2 before encoding) as a */ /* password, to encode rnd_buf3 with scrambling. */ pass_pos = 0; StrNCpy0(current_pass, rnd_buf); multiuser_encode_line(rnd_buf3, 1, 1); /* Now we have a postbox */ if ( multiuser_write_line(f, "X-Mercury-Command:Login250\n", 0) && multiuser_write_line(f, user, 1) && multiuser_write_line(f, rnd_buf2, 0) && multiuser_write_line(f, rnd_buf3, 0) && multiuser_write_line(f, "WWWAccess\n", 0) ) { /* For timeout */ login_time = time(NULL); StrNCpy0(login_password, pass); StrNCpy0(login_user, user); } else { /* Error writing to postbox */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("PBoxWErr:Error writing to server postbox. Wait a moment and try again.", 0, 0)); e = &erb; } fclose(f); /* Watch for server writing to file by remembering */ /* the current timestamp before we go into the */ /* waiting loop. */ login_stamp = multiuser_get_stamp(login_pb); return e; } /*************************************************/ /* multiuser_watch_flags() */ /* */ /* Watch the re_login and logged_in flags */ /* and deal with them if they become set. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler (this is called on null events). */ /*************************************************/ static int multiuser_watch_flags(int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle) { if (re_login || logged_in) { deregister_null_claimant(Wimp_ENull, multiuser_check_login, NULL); deregister_null_claimant(Wimp_ENull, multiuser_watch_flags, NULL); if (re_login) { /* This call clears the re_login and logged_in flags itself */ ChkError(multiuser_login()); } else { /* Show the 'normal' icon bar sprite */ show_error_ret(multiuser_set_iconbar_variant("n")); } } return 0; } /*************************************************/ /* multiuser_check_login() */ /* */ /* Parameters are as standard for a Wimp event */ /* handler (this is called on null events). */ /*************************************************/ static int multiuser_check_login(int eventcode, WimpPollBlock * b, IdBlock * idb, void * handle) { char pass [Limits_Multi_Pathname]; char key [Limits_Multi_Pathname]; char temp [Limits_Multi_Pathname]; char user_file [Limits_Multi_Pathname]; if (!multiuser_get_length(login_pb)) { /* User name of password wrong */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("BadUorPW:Incorrect user name or password.", 0, 0)); show_error_ret(&erb); re_login = 1; return 0; } /* Has the login file been modified? */ if (multiuser_get_stamp(login_pb) != login_stamp) { FILE * f; f = fopen(login_pb, "r"); /* Is the server still writing to the file? */ if (f == NULL) { time_t t = time(NULL); _swix(Hourglass_On, 0); /* Wait about 5 seconds... */ while (difftime(time(NULL), t) < 5); _swix(Hourglass_Off, 0); /* ...and try again */ f = fopen(login_pb, "r"); } /* Was the file successfully opened? */ if (f) { /* Yes; read the details */ if ( !multiuser_get_line(f, user.name, 0) || !multiuser_get_line(f, temp, 0) || !multiuser_get_line(f, user_file, 0) || !multiuser_get_line(f, temp, 0) || !multiuser_get_line(f, pass, 0) || !multiuser_get_line(f, key, 0) ) { /* There was a problem reading from the file */ multiuser_check_login_read_error: erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("PBoxRErr:Error reading from server postbox. Wait a moment and try again.", 0, 0)); show_error_ret(&erb); fclose(f); remove(login_pb); re_login = 1; return 0; } else if (strcmp(pass, rnd_buf)) { /* File contents read OK, but wrong user name or password */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("BadUorPW:Incorrect user name or password.", 0, 0)); show_error_ret(&erb); fclose(f); remove(login_pb); re_login = 1; return 0; } else { /* Password is OK, so check we are allowed WWW access. */ if ( !multiuser_get_line(f, temp, 0) || !multiuser_get_line(f, temp, 0) || !multiuser_get_line(f, temp, 0) ) { /* File read error */ goto multiuser_check_login_read_error; } else if (strcmp(temp, "Yes")) { /* Not allowed WWW access */ erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoWWW:Sorry, you are not authorised to access the World Wide Web.", 0, 0)); show_error_ret(&erb); fclose(f); remove(login_pb); /* Go back to the login screen */ re_login = 1; return 0; } /* Logged in successfully */ pass_pos = 0; // Given that the browser never does anything with this // afterwards, I've removed the field from the userdef structure // but left this here in case a good reason to put it back // ends up coming to light. // // multiuser_decode_data(key, strlen(key), rnd_buf1, 1, 1); // // StrNCpy0(user.password, key); } /* Close the temporary file and remove it */ fclose(f); remove(login_pb); /* Load history and hotlist */ // Customer didn't allow individual user home pages, // so basically this is a waste of space. Left in for // future expansion, ahem. // // StrNCpy0(user.homepage, choices.home_page); sprintf(user.hotlist_path, "%s.Web.%s.HotList", choices.post_in, user_file); sprintf(user.history_path, "%s.Web.%s.History", choices.post_in, user_file); sprintf(user.image_history_path, "%s.Web.%s.Images", choices.post_in, user_file); /* Main hotlist (will return error if file not found, */ /* so ignore errors) */ hotlist_load(user.hotlist_path); if (!strcmp(lookup_choice("LoadResources:yes",0,0), "yes")) { /* Resources file (will not return error if file not */ /* found, only if out of memory etc., so report those) */ sprintf(temp, "%s.Web.Resources", choices.post_in); show_error_ret(hotlist_load_resources(temp)); } /* Histories */ show_error_ret(history_load(user.history_path)); show_error_ret(imghistory_load(user.image_history_path)); /* Flag that the user is logged in now. */ logged_in = 1; } } /* File not modified */ else { /* Have we timed out? */ if (difftime(time(NULL), login_time) < choices.log_in_timeout) return 0; /* Yes; server isn't responding. */ remove(login_pb); erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NoServer:Server not responding.", 0, 0)); show_error_ret(&erb); re_login = 1; } return 0; } #else /*************************************************/ /* multiuser_read_user() (single user version) */ /* */ /*************************************************/ static _kernel_oserror * multiuser_read_user(const char * name, const char * password) { _kernel_oserror * e1, * e2 = NULL; #ifdef TRACE if (tl & (1u<<5)) Printf("multiuser_read_user: Loading history\n"); #endif e1 = history_load(choices.history_path); if (e1) e2 = e1; /* Similarly, load the image history */ #ifdef TRACE if (tl & (1u<<5)) Printf("multiuser_read_user: Loading image history\n"); #endif e1 = imghistory_load(choices.image_history_path); if (e1) e2 = e1; #ifndef REMOTE_HOTLIST /* Load the hotlist */ e1 = hotlist_load(choices.hotlist_path); if (e1) e2 = e1; #endif return e2; } #endif