1 /* Information about current document and current link */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/types.h>
10 #ifdef HAVE_TIME_H
11 #include <time.h>
12 #endif
13 
14 #include "elinks.h"
15 
16 #include "bfu/dialog.h"
17 #include "cache/cache.h"
18 #include "dialogs/document.h"
19 #include "document/document.h"
20 #include "document/html/renderer.h"
21 #include "document/view.h"
22 #include "globhist/globhist.h"
23 #include "intl/gettext/libintl.h"
24 #include "protocol/header.h"
25 #include "protocol/uri.h"
26 #include "session/location.h"
27 #include "session/session.h"
28 #include "terminal/terminal.h"
29 #include "terminal/window.h"
30 #include "util/conv.h"
31 #include "util/memory.h"
32 #include "util/string.h"
33 #include "viewer/text/link.h"
34 #include "viewer/text/view.h"
35 
36 void
nowhere_box(struct terminal * term,unsigned char * title)37 nowhere_box(struct terminal *term, unsigned char *title)
38 {
39 	assert(term);
40 	if_assert_failed return;
41 
42 	if (!title || !*title)
43 		title = N_("Info");
44 
45 	info_box(term, 0, title, ALIGN_CENTER,
46 		 N_("You are nowhere!"));
47 }
48 
49 static void
add_link_info_to_string(struct string * msg,struct session * ses)50 add_link_info_to_string(struct string *msg, struct session *ses)
51 {
52 	struct document_view *doc_view = current_frame(ses);
53 	struct terminal *term = ses->tab->term;
54 	unsigned char *a;
55 	struct link *link;
56 
57 	if (!doc_view) return;
58 
59 	add_char_to_string(msg, '\n');
60 
61 	a = get_current_link_info(ses, doc_view);
62 	if (a) {
63 		add_format_to_string(msg, "\n%s: %s",
64 				     _("Link", term), a);
65 		mem_free(a);
66 	}
67 
68 	a = get_current_link_title(doc_view);
69 	if (a) {
70 		add_format_to_string(msg, "\n%s: %s",
71 				     _("Link title", term), a);
72 		mem_free(a);
73 	}
74 
75 	link = get_current_link_in_view(doc_view);
76 	if (link) {
77 		struct string img;
78 #ifdef CONFIG_GLOBHIST
79 		struct global_history_item *historyitem;
80 #endif
81 
82 		if (link->where_img && init_string(&img)) {
83 			add_string_uri_to_string(&img, link->where_img,
84 						 URI_PUBLIC);
85 			decode_uri_string_for_display(&img);
86 
87 			add_format_to_string(msg, "\n%s: %s",
88 					     _("Link image", term),
89 					     img.source);
90 			done_string(&img);
91 		}
92 
93 #ifdef CONFIG_GLOBHIST
94 		historyitem = get_global_history_item(link->where);
95 		if (historyitem) {
96 			unsigned char *last_visit;
97 
98 			last_visit = ctime(&historyitem->last_visit);
99 
100 			if (last_visit)
101 				add_format_to_string(msg,
102 					"\n%s: %.24s",
103 					_("Link last visit time",
104 					  term),
105 					last_visit);
106 
107 			if (*historyitem->title)
108 				add_format_to_string(msg, "\n%s: %s",
109 					_("Link title (from history)",
110 					  term),
111 					historyitem->title);
112 		}
113 #endif
114 	}
115 }
116 
117 /* Location info. message box. */
118 void
document_info_dialog(struct session * ses)119 document_info_dialog(struct session *ses)
120 {
121 	struct terminal *term = ses->tab->term;
122 	struct location *location = cur_loc(ses);
123 	struct document_view *doc_view;
124 	struct cache_entry *cached;
125 	struct string msg;
126 
127 	if (!location) {
128 		nowhere_box(term, NULL);
129 		return;
130 	}
131 
132 	doc_view = current_frame(ses);
133 
134 	if (!init_string(&msg)) return;
135 
136 	add_to_string(&msg, _("URL", term));
137 	add_to_string(&msg, ": ");
138 
139 	/* Add the uri with password and post info stripped */
140 	add_uri_to_string(&msg, location->vs.uri, URI_PUBLIC);
141 
142 	add_char_to_string(&msg, '\n');
143 
144 	if (doc_view && doc_view->document->title) {
145 		add_format_to_string(&msg, "%s: %s", _("Title", term),
146 				     doc_view->document->title);
147 	}
148 
149 	add_char_to_string(&msg, '\n');
150 
151 	cached = find_in_cache(location->vs.uri);
152 	if (cached) {
153 		unsigned char *a;
154 
155 		add_format_to_string(&msg, "\n%s: %" OFF_T_FORMAT,
156 				     _("Size", term), cached->length);
157 
158 		if (cached->incomplete) {
159 			add_format_to_string(&msg, " (%s)", _("incomplete", term));
160 		}
161 
162 		if (doc_view) {
163 			add_format_to_string(&msg, "\n%s: %s", _("Codepage", term),
164 					get_cp_name(doc_view->document->cp));
165 
166 			if (doc_view->document->cp_status == CP_STATUS_ASSUMED) {
167 				add_format_to_string(&msg, " (%s)", _("assumed", term));
168 			} else if (doc_view->document->cp_status == CP_STATUS_IGNORED) {
169 				add_format_to_string(&msg, " (%s)",
170 						_("ignoring server setting", term));
171 			}
172 		}
173 
174 		a = parse_header(cached->head, "Server", NULL);
175 		if (a) {
176 			add_format_to_string(&msg, "\n%s: %s",
177 					     _("Server", term), a);
178 			mem_free(a);
179 		}
180 
181 		if (cached->ssl_info) {
182 			add_format_to_string(&msg, "\n%s: %s",
183 					     _("SSL Cipher", term),
184 					     cached->ssl_info);
185 		}
186 		if (cached->encoding_info) {
187 			add_format_to_string(&msg, "\n%s: %s",
188 					     _("Encoding", term),
189 					     cached->encoding_info);
190 		}
191 
192 		a = parse_header(cached->head, "Date", NULL);
193 		if (a) {
194 			add_format_to_string(&msg, "\n%s: %s",
195 					     _("Date", term), a);
196 			mem_free(a);
197 		}
198 
199 		if (cached->last_modified) {
200 			add_format_to_string(&msg, "\n%s: %s",
201 					     _("Last modified", term),
202 					     cached->last_modified);
203 		}
204 
205 	}
206 
207 #ifdef CONFIG_GLOBHIST
208 	{
209 		unsigned char *last_visit = NULL;
210 		struct global_history_item *historyitem;
211 
212 		add_format_to_string(&msg, "\n%s: ",
213 				     _("Last visit time", term));
214 
215 		historyitem = get_global_history_item(struri(location->vs.uri));
216 
217 		if (historyitem) last_visit = ctime(&historyitem->last_visit);
218 
219 		/* GNU's documentation says that ctime() can return NULL.
220 		 * The Open Group Base Specifications Issue 6 implies
221 		 * otherwise, but is ambiguous. Let's be safe. -- Miciah
222 		 */
223 		if (last_visit) {
224 			/* The string returned by ctime() includes a newline,
225 			 * and we don't want that, so we use add_bytes_to_str.
226 			 * The string always has exactly 25 characters, so add
227 			 * 24 bytes: The length of the string, minus one for
228 			 * the newline. -- Miciah
229 			 */
230 			add_bytes_to_string(&msg, last_visit, 24);
231 		} else {
232 			add_to_string(&msg, _("Unknown", term));
233 		}
234 	}
235 #endif
236 
237 	add_link_info_to_string(&msg, ses);
238 
239 	info_box(term, MSGBOX_FREE_TEXT | MSGBOX_SCROLLABLE,
240 		 N_("Info"), ALIGN_LEFT, msg.source);
241 }
242 
243 void
cached_header_dialog(struct session * ses,struct cache_entry * cached)244 cached_header_dialog(struct session *ses, struct cache_entry *cached)
245 {
246 	int msgbox_flags = 0;
247 	unsigned char *title = N_("Header info");
248 	unsigned char *headers = NULL;
249 	int i = 0, j = 0;
250 
251 	if (!cached || !cached->head || !*cached->head)
252 		goto display_headers;
253 
254 #ifdef CONFIG_DEBUG
255 	/* If |cached->head| starts with a newline, it has been
256 	 * internally generated, usually to give ELinks-generated
257 	 * documents (e.g., file:// directory listings) a MIME type
258 	 * of text/html. */
259 	if (*cached->head == '\r')
260 		title = N_("Internal header info");
261 #endif
262 
263 	headers = mem_alloc(strlen(cached->head) + 1);
264 	if (!headers) return;
265 
266 	/* Sanitize headers string. */
267 	/* XXX: Do we need to check length and limit
268 	 * it to something reasonable? */
269 
270 	while (cached->head[i]) {
271 		/* Check for control chars. */
272 		if (cached->head[i] < ' '
273 		    && cached->head[i] != '\n') {
274 			/* Ignore '\r' but replace
275 			 * other control chars with
276 			 * a visible char. */
277 			if (cached->head[i] != '\r') {
278 				 headers[j] = '*';
279 				 j++;
280 			}
281 		} else {
282 			headers[j] = cached->head[i];
283 			j++;
284 		}
285 		i++;
286 	}
287 
288 	/* Ensure null termination. */
289 	headers[j] = '\0';
290 
291 	/* Remove any trailing newlines. */
292 	while (j && headers[--j] == '\n')
293 		headers[j] = '\0';
294 
295 	if (!*headers)
296 		mem_free_set(&headers, NULL);
297 
298 display_headers:
299 
300 	if (!headers) {
301 		headers = N_("No header info.");
302 	} else {
303 		msgbox_flags = MSGBOX_FREE_TEXT | MSGBOX_SCROLLABLE;
304 	}
305 
306 	/* Headers info message box. */
307 	info_box(ses->tab->term, msgbox_flags,
308 		title, ALIGN_LEFT, headers);
309 }
310 
311 /* Headers info. message box. */
312 void
protocol_header_dialog(struct session * ses)313 protocol_header_dialog(struct session *ses)
314 {
315 	if (!have_location(ses)) {
316 		nowhere_box(ses->tab->term, N_("Header info"));
317 		return;
318 	}
319 
320 	cached_header_dialog(ses, find_in_cache(cur_loc(ses)->vs.uri));
321 }
322