📄 formhist.c
字号:
/* Implementation of a login manager for HTML forms *//* $Id: formhist.c,v 1.95.2.4 2005/05/01 22:03:23 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <string.h>#include "elinks.h"#include "bfu/dialog.h"#include "document/forms.h"#include "formhist/dialogs.h"#include "formhist/formhist.h"#include "intl/gettext/libintl.h"#include "lowlevel/home.h"#include "modules/module.h"#include "sched/session.h"#include "terminal/window.h"#include "util/base64.h"#include "util/file.h"#include "util/lists.h"#include "util/object.h"#include "util/secsave.h"#include "util/string.h"#include "viewer/text/form.h"#define FORMS_HISTORY_FILENAME "formhist"/* TODO: Remember multiple login for the same form. * TODO: Password manager GUI (here?) (in dialogs.c, of course --pasky). */static struct option_info forms_history_options[] = { INIT_OPT_BOOL("document.browse.forms", N_("Show form history dialog"), "show_formhist", 0, 0, N_("Ask if a login form should be saved to file or not.\n" "This option only disables the dialog, already saved login\n" "forms are unaffected.")), NULL_OPTION_INFO,};INIT_LIST_HEAD(saved_forms);struct form_type_name { enum form_type num; unsigned char *name;};static struct form_type_name form_type2name[] = { { FC_TEXT, "text" }, { FC_PASSWORD, "password" }, { FC_FILE, "file" }, { FC_TEXTAREA, "textarea" }, { FC_CHECKBOX, "checkbox" }, { FC_RADIO, "radio" }, { FC_SELECT, "select" }, { FC_SUBMIT, "submit" }, { FC_IMAGE, "image" }, { FC_HIDDEN, "hidden" },};#define FORM_TYPE_COUNT (sizeof(form_type2name)/sizeof(struct form_type_name))intstr2form_type(unsigned char *s){ int n; for (n = 0; n < FORM_TYPE_COUNT; n++) if (!strcmp(form_type2name[n].name, s)) return form_type2name[n].num; return -1;}unsigned char *form_type2str(enum form_type num){ int n; for (n = 0; n < FORM_TYPE_COUNT; n++) if (form_type2name[n].num == num) return form_type2name[n].name; return NULL;}#undef FORM_TYPE_COUNTstatic struct formhist_data *new_form(unsigned char *url){ struct formhist_data *form; int url_len = strlen(url); form = mem_calloc(1, sizeof(*form) + url_len); if (!form) return NULL; memcpy(form->url, url, url_len); form->submit = mem_alloc(sizeof(*form->submit)); if (!form->submit) { mem_free(form); return NULL; } object_nolock(form, "formhist"); init_list(*form->submit); form->box_item = add_listbox_leaf(&formhist_browser, NULL, form); return form;}voidfree_form(struct formhist_data *form){ done_submitted_value_list(form->submit); mem_free(form->submit); if (form->box_item) done_listbox_item(&formhist_browser, form->box_item); mem_free(form);}static int loaded = 0;intload_forms_from_file(void){ struct formhist_data *form; unsigned char tmp[MAX_STR_LEN]; unsigned char *file; FILE *f; if (loaded) return 1; if (!elinks_home) return 0; file = straconcat(elinks_home, FORMS_HISTORY_FILENAME, NULL); if (!file) return 0; f = fopen(file, "rb"); mem_free(file); if (!f) return 0; while (fgets(tmp, MAX_STR_LEN, f)) { unsigned char *p; int dontsave = 0; if (tmp[0] == '\n' && !tmp[1]) continue; p = strchr(tmp, '\t'); if (p) { *p = '\0'; ++p; if (!strcmp(tmp, "dontsave")) dontsave = 1; } else { /* Compat. with older file formats. Remove it at some * time. --Zas */ if (!strncmp(tmp, "dontsave,", 9)) { dontsave = 1; p = tmp + 9; } else { p = tmp; } } /* URL */ p[strlen(p) - 1] = '\0'; form = new_form(p); if (!form) continue; if (dontsave) form->dontsave = 1; /* Fields type, name, value */ while (fgets(tmp, MAX_STR_LEN, f)) { struct submitted_value *sv; unsigned char *type, *name, *value; unsigned char *enc_value; enum form_type ftype; int ret; if (tmp[0] == '\n' && !tmp[1]) break; /* Type */ type = tmp; p = strchr(type, '\t'); if (!p) goto fail; *p = '\0'; /* Name */ name = ++p; p = strchr(name, '\t'); if (!p) { /* Compatibility with previous file formats. * REMOVE AT SOME TIME --Zas */ value = name; name = type; if (*name == '*') { name++; type = "password"; } else { type = "text"; } goto cont; } *p = '\0'; /* Value */ value = ++p;cont: p = strchr(value, '\n'); if (!p) goto fail; *p = '\0'; ret = str2form_type(type); if (ret == -1) goto fail; ftype = ret; if (form->dontsave) continue; enc_value = *value ? base64_decode(value) : stracpy(value); if (!enc_value) goto fail; sv = init_submitted_value(name, enc_value, ftype, NULL, 0); mem_free(enc_value); if (!sv) goto fail; add_to_list(*form->submit, sv); } add_to_list(saved_forms, form); } fclose(f); loaded = 1; return 1;fail: free_form(form); return 0;}intsave_forms_to_file(void){ struct secure_save_info *ssi; unsigned char *file; struct formhist_data *form; int r; if (!elinks_home || get_cmd_opt_bool("anonymous")) return 0; file = straconcat(elinks_home, FORMS_HISTORY_FILENAME, NULL); if (!file) return 0; ssi = secure_open(file, 0177); mem_free(file); if (!ssi) return 0; /* Write the list to password file ($ELINKS_HOME/formhist) */ foreach (form, saved_forms) { struct submitted_value *sv; if (form->dontsave) { secure_fprintf(ssi, "dontsave\t%s\n\n", form->url); continue; } secure_fprintf(ssi, "%s\n", form->url); foreach (sv, *form->submit) { unsigned char *encvalue; if (sv->value && *sv->value) { /* Obfuscate the value. If we do * $ cat ~/.elinks/formhist * we don't want someone behind our back to read our * password (androids don't count). */ encvalue = base64_encode(sv->value); } else { encvalue = stracpy(""); } if (!encvalue) return 0; /* Format is : type[TAB]name[TAB]value[CR] */ secure_fprintf(ssi, "%s\t%s\t%s\n", form_type2str(sv->type), sv->name, encvalue); mem_free(encvalue); } secure_fputc(ssi, '\n'); } r = secure_close(ssi); if (r == 0) loaded = 1; return r;}/* Check whether the form (chain of @submit submitted_values at @url document) * is already present in the form history. */static intform_exists(struct formhist_data *form1){ struct formhist_data *form; if (!load_forms_from_file()) return 0; foreach (form, saved_forms) { int count = 0; int exact = 0; struct submitted_value *sv; if (strcmp(form->url, form1->url)) continue; if (form->dontsave) return 1; /* Iterate through submitted entries. */ foreach (sv, *form1->submit) { struct submitted_value *sv2; unsigned char *value = NULL; count++; foreach (sv2, *form->submit) { if (sv->type != sv2->type) continue; if (!strcmp(sv->name, sv2->name)) { exact++; value = sv2->value; break; } } /* If we found a value for that name, check if value * has changed or not. */ if (value && strcmp(sv->value, value)) return 0; } /* Check if submitted values have changed or not. */ if (count && exact && count == exact) return 1; } return 0;}static intforget_forms_with_url(unsigned char *url){ struct formhist_data *form, *tmpform; int count = 0; foreach (form, saved_forms) { if (strcmp(form->url, url)) continue; tmpform = form; form = form->prev; del_from_list(tmpform); free_form(tmpform); count++; } return count;}/* Appends form data @form1 (url and submitted_value(s)) to the password file. * Returns 1 on success, 0 otherwise. */static intremember_form(struct formhist_data *form){ forget_forms_with_url(form->url); add_to_list(saved_forms, form); return save_forms_to_file();}static intnever_for_this_site(struct formhist_data *form){ form->dontsave = 1; return remember_form(form);}unsigned char *get_form_history_value(unsigned char *url, unsigned char *name){ struct formhist_data *form; if (!url || !*url || !name || !*name) return NULL; if (!load_forms_from_file()) return NULL; foreach (form, saved_forms) { if (form->dontsave) continue; if (!strcmp(form->url, url)) { struct submitted_value *sv; foreach (sv, *form->submit) if (!strcmp(sv->name, name)) return sv->value; } } return NULL;}voidmemorize_form(struct session *ses, struct list_head *submit, struct form *forminfo){ struct formhist_data *form; struct submitted_value *sv; int save = 0; /* XXX: For now, we only save these types of form fields. */ foreach (sv, *submit) { if (sv->type == FC_PASSWORD && sv->value && *sv->value) { save = 1; break; } } if (!save) return; /* Create a temporary form. */ form = new_form(forminfo->action); if (!form) return; foreach (sv, *submit) { if ((sv->type == FC_TEXT) || (sv->type == FC_PASSWORD)) { struct submitted_value *sv2; sv2 = init_submitted_value(sv->name, sv->value, sv->type, NULL, 0); if (!sv2) goto fail; add_to_list(*form->submit, sv2); } } if (form_exists(form)) goto fail; msg_box(ses->tab->term, NULL, 0, N_("Form history"), ALIGN_CENTER, N_("Should this login be remembered?\n\n" "Please note that the password will be stored " "obscured (but unencrypted) in a file on your disk.\n\n" "If you are using a valuable password, answer NO."), form, 3, N_("~Yes"), remember_form, B_ENTER, N_("~No"), free_form, B_ESC, N_("Ne~ver for this site"), never_for_this_site, NULL); return;fail: free_form(form);}static voiddone_form_history(struct module *module){ struct formhist_data *form; foreach(form, saved_forms) { done_submitted_value_list(form->submit); mem_free(form->submit); if (form->box_item) done_listbox_item(&formhist_browser, form->box_item); } free_list(saved_forms);}struct module forms_history_module = struct_module( /* name: */ N_("Form History"), /* options: */ forms_history_options, /* events: */ NULL, /* submodules: */ NULL, /* data: */ NULL, /* init: */ NULL, /* done: */ done_form_history);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -