wdvi

network DVI viewer
Log | Files | Refs

commit 7866ab6cf117b51f328d4426d209ee79c4e2f2e9
parent 0959598e5774d02628fa74a12f7bc93d3d29960d
Author: Kyle Milz <krwmilz@gmail.com>
Date:   Thu, 19 Aug 2021 17:10:40 +0000

fetch dvi files from network

Add basic dvi fetching over HTTPS. Use openssl to make TLS connections to a
remote machine to fetch documents.

Add an address bar widget that the user can edit, and fetch documents from.

Render the fetched document in the main viewport window.

Diffstat:
MMakefile.in | 25+++++++++++++------------
Mdvi-draw.c | 40++++++++++++++++++++--------------------
Mdvi-init.c | 2++
Ahttp.c | 443+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahttp.h | 5+++++
Mxdvi.c | 103++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mxdvi.h | 2+-
7 files changed, 547 insertions(+), 73 deletions(-)

diff --git a/Makefile.in b/Makefile.in @@ -33,13 +33,13 @@ EXTRA_CPPFLAGS=@EXTRA_CPPFLAGS@ LDFLAGS=@LDFLAGS@ X_LDFLAGS=@LDFLAGS@ @x_linker_options@ @X_LIBS@ X_LIBS=@X_PRE_LIBS@ -lX11 @X_EXTRA_LIBS@ -EXTRA_LIBS=@EXTRA_LIBS@ +EXTRA_LIBS=@EXTRA_LIBS@ -lcrypto -lssl LIBS=@LIBS@ @MATH_LIB@ SRCS=xdvi.c events.c dvi-init.c dvi-draw.c special.c font-open.c filefind.c \ - pk.c vf.c util.c@OPT_SRCS@ + pk.c vf.c util.c http.c@OPT_SRCS@ OBJS=xdvi.o events.o dvi-init.o dvi-draw.o special.o font-open.o filefind.o \ - pk.o vf.o util.o@OPT_OBJS@ + pk.o vf.o util.o http.o@OPT_OBJS@ INSTALL=@INSTALL@ INSTALL_PROGRAM=@INSTALL_PROGRAM@ @@ -48,8 +48,8 @@ INSTALL_DATA=@INSTALL_DATA@ all: xdvi$(EXEEXT) xdvi.man xdvi$(EXEEXT): $(OBJS) - $(CC) $(X_LDFLAGS) -o xdvi $(OBJS) $(X_LIBS) $(EXTRA_LIBS) $(LIBS) - $(CHMOD) go+rx xdvi$(EXEEXT) + $(CC) $(X_LDFLAGS) -o wdvi $(OBJS) $(X_LIBS) $(EXTRA_LIBS) $(LIBS) + $(CHMOD) go+rx wdvi$(EXEEXT) .c.o: $(CC) -c $(CFLAGS) $(X_CFLAGS) $(EXTRA_CPPFLAGS) $< @@ -70,6 +70,7 @@ psgs.o: config.h xdvi.h font-open.o: config.h xdvi.h filf-app.h filefind.h special.o: config.h xdvi.h filf-app.h filefind.h filefind.o: config.h xdvi.h filf-app.h filefind.h +http.o: config.h xdvi.h krheader.h: xdvi.c config.h xdvi.h Makefile $(CPP) -DMAKING_HEADER $(srcdir)/xdvi.c \ @@ -92,9 +93,9 @@ xdvi.man: xdvi-man.sed mksedscript Makefile config.h check: -install-bin: xdvi$(EXEEXT) - strip xdvi$(EXEEXT) - $(INSTALL_PROGRAM) xdvi$(EXEEXT) ${bindir}/xdvi$(EXEEXT) +install-bin: wdvi$(EXEEXT) + strip wdvi$(EXEEXT) + $(INSTALL_PROGRAM) wdvi$(EXEEXT) ${bindir}/wdvi$(EXEEXT) $(INSTALL_PROGRAM) $(srcdir)/xdvizilla ${bindir}/xdvizilla install-man: xdvi.man @@ -111,7 +112,7 @@ install-data: config.xdvi install: install-bin install-man install-data uninstall: - -$(RM) ${bindir}/xdvi$(EXEEXT) ${mandir}/man$(MANEXT)/xdvi.$(MANEXT) + -$(RM) ${bindir}/wdvi$(EXEEXT) ${mandir}/man$(MANEXT)/xdvi.$(MANEXT) -$(RM) ${bindir}/xdvizilla ${mandir}/man$(MANEXT)/xdvizilla.$(MANEXT) @if grep "original config.xdvi --" "$(datadir)$(dvipsconfdir)/config.xdvi" >/dev/null 2>&1 \ || test ! -r "$(datadir)$(dvipsconfdir)/config.xdvi"; then \ @@ -123,14 +124,14 @@ TAGS: $(srcs) etags $(srcs) mostlyclean: - -$(RM) *.o xdvi$(EXEEXT) xdvi.man core sedscript *~ + -$(RM) *.o wdvi$(EXEEXT) xdvi.man core sedscript *~ archclean: - -$(RM) *.o xdvi$(EXEEXT) Makefile + -$(RM) *.o wdvi$(EXEEXT) Makefile -$(RM) config.h config.cache config.status config.log clean: - -$(RM) *.o xdvi$(EXEEXT) xdvi.man core sedscript krheader.h *~ + -$(RM) *.o wdvi$(EXEEXT) xdvi.man core sedscript krheader.h *~ -$(RM) psheader.c squeeze$(EXEEXT) distclean: clean diff --git a/dvi-draw.c b/dvi-draw.c @@ -223,7 +223,7 @@ put_image(g, x, y) * Byte reading routines for dvi file. */ -#define xtell(pos) (lseek(fileno(dvi_file), 0L, SEEK_CUR) - \ +#define xtell(pos) (fseek(dvi_file, 0L, SEEK_CUR) - \ (currinf.end - (pos))) static ubyte @@ -234,8 +234,8 @@ xxone() return EOP; } currinf.end = dvi_buffer + - read(fileno(dvi_file), (char *) (currinf.pos = dvi_buffer), - DVI_BUFFER_LEN); + fread((char *) (currinf.pos = dvi_buffer), 1, DVI_BUFFER_LEN, + dvi_file); return currinf.end > dvi_buffer ? *(currinf.pos)++ : EOF; } @@ -275,7 +275,7 @@ xskip(offset) { currinf.pos += offset; if (!currinf.virtual && currinf.pos > currinf.end) - (void) lseek(fileno(dvi_file), (long) (currinf.pos - currinf.end), + (void) fseek(dvi_file, (long) (currinf.pos - currinf.end), SEEK_CUR); } @@ -293,7 +293,7 @@ tell_oops(const char *message, ...) if (currinf.virtual) Fprintf(stderr, " in virtual font %s\n", currinf.virtual->fontname); else - Fprintf(stderr, ", offset %jd\n", xtell(currinf.pos - 1)); + Fprintf(stderr, ", offset %ld\n", xtell(currinf.pos - 1)); xdvi_exit(1); } @@ -824,7 +824,7 @@ prescan() dvi_file_ready = False; nextreportpage = scanned_page; - (void) lseek(fileno(dvi_file), page_info[scanned_page + 1].offset, + (void) fseek(dvi_file, page_info[scanned_page + 1].offset, SEEK_SET); currinf.pos = currinf.end = dvi_buffer; @@ -1368,7 +1368,7 @@ draw_part(minframe, current_dimconv) oldinfo.end = currinf.end; } else { - (void) lseek(fileno(dvi_file), file_pos, + (void) fseek(dvi_file, file_pos, SEEK_SET); oldinfo.pos = oldinfo.end; } @@ -1513,7 +1513,7 @@ draw_page() ROUNDUP(page_info[current_page].pw, shrink_factor) + 1, ROUNDUP(page_info[current_page].ph, shrink_factor) + 1); - (void) lseek(fileno(dvi_file), page_info[current_page].offset, + (void) fseek(dvi_file, page_info[current_page].offset, SEEK_SET); bzero((char *) &currinf.data, sizeof currinf.data); @@ -1884,7 +1884,7 @@ geom_scan_part(g_info, minframe, current_dimconv) oldinfo.end = currinf.end; } else { - (void) lseek(fileno(dvi_file), file_pos, + (void) fseek(dvi_file, file_pos, SEEK_SET); oldinfo.pos = oldinfo.end; } @@ -2027,9 +2027,9 @@ geom_scan(g_info) #endif if (dvi_pointer_frame != NULL) - pos_save = lseek(fileno(dvi_file), 0L, SEEK_CUR) + pos_save = fseek(dvi_file, 0L, SEEK_CUR) - (dvi_pointer_frame->end - dvi_pointer_frame->pos); - (void) lseek(fileno(dvi_file), page_info[current_page].offset, + (void) fseek(dvi_file, page_info[current_page].offset, SEEK_SET); currinf_save = currinf; @@ -2049,7 +2049,7 @@ geom_scan(g_info) currinf = currinf_save; if (dvi_pointer_frame != NULL) { - (void) lseek(fileno(dvi_file), pos_save, SEEK_SET); + (void) fseek(dvi_file, pos_save, SEEK_SET); dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; } } @@ -2527,7 +2527,7 @@ source_reverse_search(x, y) /* Save file position */ if (dvi_pointer_frame != NULL) - pos_save = lseek(fileno(dvi_file), 0L, SEEK_CUR) + pos_save = fseek(dvi_file, 0L, SEEK_CUR) - (dvi_pointer_frame->end - dvi_pointer_frame->pos); currinf_save = currinf; @@ -2538,7 +2538,7 @@ source_reverse_search(x, y) for (;;) { if (++upper < total_pages) { - (void) lseek(fileno(dvi_file), page_info[upper].offset, + (void) fseek(dvi_file, page_info[upper].offset, SEEK_SET); bzero((char *) &currinf.data, sizeof currinf.data); currinf.tn_table_len = TNTABLELEN; @@ -2560,7 +2560,7 @@ source_reverse_search(x, y) break; if (--lower >= 0) { - (void) lseek(fileno(dvi_file), page_info[lower].offset, + (void) fseek(dvi_file, page_info[lower].offset, SEEK_SET); bzero((char *) &currinf.data, sizeof currinf.data); currinf.tn_table_len = TNTABLELEN; @@ -2591,7 +2591,7 @@ source_reverse_search(x, y) currinf = currinf_save; if (dvi_pointer_frame != NULL) { - (void) lseek(fileno(dvi_file), pos_save, SEEK_SET); + (void) fseek(dvi_file, pos_save, SEEK_SET); dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; } @@ -2886,9 +2886,9 @@ source_forward_search(str) drawing). */ if (dvi_pointer_frame != NULL) - pos_save = lseek(fileno(dvi_file), 0L, SEEK_CUR) + pos_save = fseek(dvi_file, 0L, SEEK_CUR) - (dvi_pointer_frame->end - dvi_pointer_frame->pos); - (void) lseek(fileno(dvi_file), page_info[0].offset, SEEK_SET); + (void) fseek(dvi_file, page_info[0].offset, SEEK_SET); currinf_save = currinf; maxchar_save = maxchar; @@ -2925,7 +2925,7 @@ source_forward_search(str) currinf = currinf_save; if (dvi_pointer_frame != NULL) { - (void) lseek(fileno(dvi_file), pos_save, SEEK_SET); + (void) fseek(dvi_file, pos_save, SEEK_SET); dvi_pointer_frame->pos = dvi_pointer_frame->end = dvi_buffer; } @@ -2967,7 +2967,7 @@ source_forward_search(str) -0x7fffffff; source_fwd_box_page = -1; /* in case of error, suppress box */ - (void) lseek(fileno(dvi_file), page_info[best_page].offset, SEEK_SET); + (void) fseek(dvi_file, page_info[best_page].offset, SEEK_SET); currinf.tn_table_len = TNTABLELEN; currinf.tn_table = tn_table; currinf.tn_head = tn_head; diff --git a/dvi-init.c b/dvi-init.c @@ -922,6 +922,8 @@ check_dvi_file() if (!dvi_is_valid) return False; + return True; + if (dvi_file == NULL) { /* check if file exists and has changed */ if (!(ev_flags & EV_NEWDOC) && stat(dvi_name, &buf) == 0 diff --git a/http.c b/http.c @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2020 Kyle Milz <krwmilz@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/socket.h> /* connect(), socket() */ + +#include <arpa/inet.h> /* inet_ntoa() */ +#include <netdb.h> /* gethostbyname() */ +#include <netinet/in.h> /* struct sockaddr_in */ + +#include <assert.h> +#include <err.h> /* err() */ +#include <stdio.h> /* sscanf, fmemopen */ +#include <stdlib.h> /* strtonum() */ +#include <unistd.h> /* close() */ + +#include <openssl/err.h> /* ERR_load_crypto_strings() */ +#include <openssl/evp.h> /* OpenSSL_add_all_algorithms() */ +#include <openssl/ssl.h> + +#include "xdvi.h" /* warning_popup_long() */ +#include "util.h" /* xmalloc() */ + + +#define SSL_BUF_SIZE 16 * 1024 + + +struct url { + const char *orig_url; + char hostname[128]; + int port; + char document[128]; +}; + +struct http_headers { + char proto[9]; /* HTTP/1.1 or whatever */ + int status; /* 200, 404, etc */ + char status_text[32]; /* 416 may be the longest? */ + int content_len; /* Content-Length: */ +}; + + +/* + * Try and parse dest_url[] as a url: + * - ignore protocol if found + * - hostname is either what's remaining or up to a colon or slash character + * - if colon is found, port number is either what is remaining or up to slash + * character + * - if slash is found the rest of the string is a document path + * + * On successful parse a filled in struct url is returned that the caller must + * free, otherwise a NULL pointer is returned. + */ +static struct url * +parse_url(const char dest_url[]) +{ + struct url *url; + const char *hostname_end; + const char *proto_loc; + const char *colon_loc; + const char *slash_loc; + char portnum[6]; + const char *errstr; + + url = xmalloc(sizeof(struct url)); + url->port = 443; + url->orig_url = dest_url; + + hostname_end = dest_url + strlen(dest_url); + + /* Skip protocol if found. */ + if ((proto_loc = strstr(dest_url, "://"))) + dest_url = proto_loc + strlen("://"); + + colon_loc = strchr(dest_url, ':'); + slash_loc = strchr(dest_url, '/'); + + /* Try and parse port number if found. */ + if (colon_loc) { + hostname_end = colon_loc; + + if (slash_loc) + strlcpy(portnum, colon_loc + 1, slash_loc - colon_loc); + else + strlcpy(portnum, colon_loc + 1, sizeof(portnum)); + + /* Port numbers cannot be larger than 65535. */ + url->port = strtonum(portnum, 1, 64 * 1024 - 1, &errstr); + if (errstr != NULL) { + warning_popup_long("%s: port number '%s': %s", + "OK", NULL, + dest_url, portnum, errstr); + return NULL; + } + } + + /* Set document path if found. */ + if (slash_loc) { + strlcpy(url->document, slash_loc, sizeof(url->document)); + + if (colon_loc == NULL) + hostname_end = slash_loc; + } else + strcpy(url->document, "/"); + + strlcpy(url->hostname, dest_url, hostname_end - dest_url + 1); + + return url; +} + + +/* + * Takes a struct url and resolves the url hostname. Then tries connecting to + * the url hostname using the url port. + * + * On success return an open socket represented by a non negative integer or + * -1 on failure. + */ +static int +xconnect(struct url *url) +{ + struct hostent *host; + int sockfd; + struct sockaddr_in dest_addr; + + if ((host = gethostbyname(url->hostname)) == NULL) { + warning_popup_long("%s: %s", "OK", NULL, url->hostname, + hstrerror(h_errno)); + return -1; + } + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + err(1, "socket"); + + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(url->port); + dest_addr.sin_addr.s_addr = *(long *)(host->h_addr); + + memset(&(dest_addr.sin_zero), '\0', 8); + + if (connect(sockfd, (struct sockaddr *) &dest_addr, + sizeof(struct sockaddr)) == -1) { + warning_popup_long("%s:%i: %s", "OK", NULL, + url->hostname, url->port, strerror(errno)); + close(sockfd); + return -1; + } + + return sockfd; +} + + +/* + * Map some SSL error codes to real messages. This should exist somewhere + * already? + * + * Returns a message and never fails. + */ +static const char * +SSL_strerror(const SSL *tls, int ret) +{ + switch (SSL_get_error(tls, ret)) { + case SSL_ERROR_NONE: + return "No error"; + case SSL_ERROR_ZERO_RETURN: + return "TLS connection has been closed"; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + case SSL_ERROR_WANT_X509_LOOKUP: + return "TLS operation did not complete"; + case SSL_ERROR_SYSCALL: + return "I/O error occurred"; + case SSL_ERROR_SSL: + return "Failure in SSL library"; + } + + return "Unknown TLS error"; +} + + +/* + * Connect to the host using TLS. + * + * Returns an SSL pointer on success or NULL on failure. + */ +static SSL * +tls_connect(int server, struct url *url) +{ + const SSL_METHOD *method; + SSL_CTX *ctx; + SSL *tls; + X509 *cert = NULL; + X509_NAME *certname = NULL; + BIO *outbio; + int ret, j, fprint_size; + const EVP_MD *fprint_type; + unsigned char fprint[EVP_MAX_MD_SIZE]; + EVP_PKEY *pkey = NULL; + + + /* According to LibreSSL manual pages none of these are necessary. */ + OpenSSL_add_all_algorithms(); + ERR_load_BIO_strings(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + + /* Not needed for newer (> 1.1.0) library versions? */ + assert(SSL_library_init() == 1); + + method = TLS_client_method(); + + if ((ctx = SSL_CTX_new(method)) == NULL) + errx(1, "Unable to create new SSL context structure.\n"); + + if ((tls = SSL_new(ctx)) == NULL) + errx(1, "Unable to create new SSL structure.\n"); + + SSL_set_fd(tls, server); + + if ((ret = SSL_connect(tls)) != 1) { + warning_popup_long("%s:%i: Cannot make TLS connection", + "OK", NULL, url->hostname, url->port); + + /* Print SSL_strerror(tls, ret)); too? */ + return NULL; + } + + if ((cert = SSL_get_peer_certificate(tls)) == NULL) { + warning_popup_long("%s:%i: No TLS certificate was presented by peer", + "OK", NULL, url->hostname, url->port); + return NULL; + } + + certname = X509_NAME_new(); + certname = X509_get_subject_name(cert); + + outbio = BIO_new_fp(stdout, BIO_NOCLOSE); + BIO_printf(outbio, "Certificate subject data: "); + X509_NAME_print_ex(outbio, certname, 0, 0); + BIO_printf(outbio, "\n"); + + /* Calculate certificate fingerprint. */ + fprint_type = EVP_sha256(); + + if (!X509_digest(cert, fprint_type, fprint, &fprint_size)) { + warning_popup_long("%s:%i: Cannot create TLS certificate fingerprint", + "OK", NULL, url->hostname, url->port); + return NULL; + } + + BIO_printf(outbio, "Fingerprint (method = %s, size = %d): ", + OBJ_nid2sn(EVP_MD_type(fprint_type)), fprint_size); + + for (j = 0; j < fprint_size; j++) { + BIO_printf(outbio, "%02X%c", fprint[j], + (j + 1 == fprint_size) ? '\n' : ':'); + } + BIO_printf(outbio, "\n"); + + /* Get certificate public key. */ + if ((pkey = X509_get_pubkey(cert)) == NULL) { + warning_popup_long("%s:%i: No public key in certificate.", + "OK", NULL, url->hostname, url->port); + return NULL; + } + PEM_write_bio_PUBKEY(outbio, pkey); + + X509_free(cert); + BIO_free_all(outbio); + + return tls; +} + + +static int +send_request(SSL *tls, struct url *url) +{ + const char *req_fmt; + char *req; + int ret; + + req_fmt = "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "\r\n"; + + if (asprintf(&req, req_fmt, url->document, url->hostname) == -1) + err(1, "asprintf"); + + if ((ret = SSL_write(tls, req, strlen(req))) < 1) { + warning_popup_long("%s: %s", "OK", NULL, url->hostname, + SSL_strerror(tls, ret)); + free(req); + return 1; + } + + free(req); + return 0; +} + + +/* + * Parse a buffer that supposedly contains HTTP headers. + * + * Return a pointer to an allocated struct http_headers that the caller must + * free. + */ +static struct http_headers * +parse_headers(char *buf, int bytes_read) +{ + char header[32], value[32]; /* unbounded len? */ + struct http_headers *res; + int pos; + + if ((res = calloc(1, sizeof(struct http_headers))) == NULL) + err(1, NULL); + + /* First line of response is 'HTTP/1.1 200 OK' for example. */ + sscanf(buf, "%8s %i %31[^\r\n]%n", res->proto, &res->status, + res->status_text, &pos); + buf += pos; + + while (sscanf(buf, "%31s %31[^\r\n]%n", header, value, &pos) != EOF) { + if (strcmp(header, "Content-Length:") == 0) + res->content_len = atoi(value); + + buf += pos; + } + + return res; +} + + +static FILE * +read_response(SSL *tls, struct url *url) +{ + char *buf; + struct http_headers *response; + int nbytes; + FILE *dvi_file = NULL; + + /* This buffer must be null terminated. */ + buf = calloc(1, SSL_BUF_SIZE); + + /* First read yields the HTTP header response in its entirety. */ + if ((nbytes = SSL_read(tls, buf, SSL_BUF_SIZE)) < 1) { + warning_popup_long("SSL_read: %s: %s", "OK", NULL, + url->hostname, SSL_strerror(tls, nbytes)); + goto free_buf; + } + + /* We must free returned response. */ + response = parse_headers(buf, nbytes); + + if (response->status != 200) { + warning_popup_long("%s: %i %s", "OK", NULL, url->orig_url, + response->status, response->status_text); + goto free_response; + } + + /* Second (or more) reads are the HTTP body response. */ + if ((nbytes = SSL_read(tls, buf, SSL_BUF_SIZE)) < 1) { + warning_popup_long("SSL_read: %s: %s", "OK", NULL, + url->hostname, SSL_strerror(tls, nbytes)); + goto free_response; + } + + /* XDvi does all it's processing with FILE streams. */ + if ((dvi_file = fmemopen(NULL, nbytes, "r+")) == NULL) + err(1, "fmemopen"); + + if (fwrite(buf, 1, nbytes, dvi_file) < nbytes) + err(1, "fwrite"); + + if (fseek(dvi_file, 0L, SEEK_SET) < 0) + err(1, "fseek"); + + printf("Number of HTTP body bytes: %i\n", nbytes); + + if (nbytes != response->content_len) + warning_popup_long("HTTP body len: expected %i but read %i\n", + "OK", NULL, response->content_len, nbytes); + +free_response: + free(response); +free_buf: + free(buf); + + return dvi_file; +} + +/* + * Gets the dest_url[] using a tls connection to the host, then writes the + * returned dvi document into a temporary file, which it returns. + * + * Returns a FILE * pointer on success or NULL pointer on failure. + */ +FILE * +http_get(const char dest_url[]) +{ + struct url *url; + int server; + SSL *tls; + FILE *dvi_file = NULL; + + if ((url = parse_url(dest_url)) == NULL) + return NULL; + + if ((server = xconnect(url)) < 0) + goto free_url; + + if ((tls = tls_connect(server, url)) == NULL) + goto free_server; + + if (send_request(tls, url)) + goto free_tls; + + if ((dvi_file = read_response(tls, url)) == NULL) + goto free_tls; + +free_tls: + SSL_CTX_free(SSL_get_SSL_CTX(tls)); + SSL_free(tls); +free_server: + close(server); +free_url: + free(url); + + return dvi_file; +} diff --git a/http.h b/http.h @@ -0,0 +1,5 @@ +#include <stdio.h> /* FILE, fseek(), fwrite(), tmpfile() */ + +static const char addr_default[] = "www.0x30.net/resume.dvi"; + +FILE *http_get(const char[]); diff --git a/xdvi.c b/xdvi.c @@ -73,6 +73,7 @@ NOTE: */ #include "xdvi.h" +#include "http.h" #if !lint static char copyright[] UNUSED = @@ -127,17 +128,9 @@ static char copyright[] UNUSED = #include <X11/Shell.h> /* needed for def. of XtNiconX */ - -# include <X11/Xaw/Viewport.h> -#define VPORT_WIDGET_CLASS viewportWidgetClass -#define DRAW_WIDGET_CLASS drawWidgetClass - -#ifdef BUTTONS -# include <X11/Xaw/Command.h> -#define FORM_WIDGET_CLASS formWidgetClass -#endif /* BUTTONS */ - - +#include <X11/Xaw/Viewport.h> +#include <X11/Xaw/AsciiText.h> +#include <X11/Xaw/Command.h> # if HAVE_X11_INTRINSICI_H # include <X11/IntrinsicI.h> @@ -778,13 +771,11 @@ static WidgetClassRec drawingWidgetClass = { #endif /* NOQUERY */ static Arg vport_args[] = { -#ifdef BUTTONS {XtNborderWidth, (XtArgVal) 0}, {XtNtop, (XtArgVal) XtChainTop}, {XtNbottom, (XtArgVal) XtChainBottom}, {XtNleft, (XtArgVal) XtChainLeft}, {XtNright, (XtArgVal) XtChainRight}, -#endif {XtNallowHoriz, (XtArgVal) True}, {XtNallowVert, (XtArgVal) True}, }; @@ -797,12 +788,17 @@ static Arg draw_args[] = { {XtNlabel, (XtArgVal) ""}, }; -#if BUTTONS +static Arg addr_args[] = { + {XtNwidth, (XtArgVal) 0}, + {XtNfromVert, (XtArgVal) NULL}, + {XtNdataCompression, (XtArgVal) False}, + {XtNeditType, (XtArgVal) XawtextEdit}, + {XtNstring, (XtArgVal) addr_default}, +}; + static Arg form_args[] = { {XtNdefaultDistance, (XtArgVal) 0}, }; -#endif - static void usage(const char *, ...) NORETURN; @@ -1611,6 +1607,35 @@ compile_wheel_actions() *wactpp = NULL; } +static void +cb_addr_go(Widget w, XtPointer client_data, XtPointer call_data) +{ + const char *s; + + if (w == NULL) + s = addr_default; + else + XtVaGetValues(addr_widget, XtNstring, &s, NULL); + + printf(">>> cb_addr_go: '%s'\n", s); + + dvi_file = http_get(s); + if (dvi_file == NULL) + return; + + ev_flags |= EV_NEWDOC; +} + +static void +act_addr_go P4C(Widget, w, XEvent *, event, String *, params, + Cardinal *, num_params) +{ + cb_addr_go(w, NULL, NULL); +} + +static XtActionsRec addr_actions[] = { + {"addr-go", act_addr_go} +}; /* @@ -1844,7 +1869,9 @@ or invalid argument:\n\t\"%s\", \"%s\".", if (dvi_name == NULL) { postpone_popups = False; - Act_open_dvi_file(NULL, NULL, NULL, 0); + act_addr_go(NULL, NULL, NULL, 0); + dvi_name = "resume.dvi"; + // Act_open_dvi_file(NULL, NULL, NULL, 0); (void) read_events(EV_GE_NEWDOC); ev_flags &= ~EV_NEWDOC; postpone_popups = True; @@ -2097,21 +2124,11 @@ argument is to override any papersize specials in the dvi file.\n\n", stderr); db = XtScreenDatabase(SCRN); - XrmPutStringResource(&db, -# if BUTTONS - "XDvi.form.baseTranslations", -# else - "XDvi.vport.baseTranslations", -# endif + XrmPutStringResource(&db, "XDvi.form.baseTranslations", base_translations); if (resource.main_translations != NULL) - XrmPutStringResource(&db, -# if BUTTONS - "XDvi.form.translations", -# else - "XDvi.vport.translations", -# endif + XrmPutStringResource(&db, "XDvi.form.translations", resource.main_translations); @@ -2119,21 +2136,13 @@ argument is to override any papersize specials in the dvi file.\n\n", stderr); * not change db (X11R6.4 source). */ } -#if BUTTONS - form_widget = XtCreateManagedWidget("form", FORM_WIDGET_CLASS, + form_widget = XtCreateManagedWidget("form", formWidgetClass, top_level, form_args, XtNumber(form_args)); #define form_or_top form_widget /* for calls later on */ #define form_or_vport form_widget -#else /* not BUTTONS */ - -#define form_or_top top_level /* for calls later on */ -#define form_or_vport vport_widget - -#endif /* not BUTTONS */ - - vport_widget = XtCreateManagedWidget("vport", VPORT_WIDGET_CLASS, + vport_widget = XtCreateManagedWidget("vport", viewportWidgetClass, form_or_top, vport_args, XtNumber(vport_args)); @@ -2142,10 +2151,24 @@ argument is to override any papersize specials in the dvi file.\n\n", stderr); draw_args[0].value = (XtArgVal) page_w; draw_args[1].value = (XtArgVal) page_h; - draw_widget = XtCreateManagedWidget("drawing", DRAW_WIDGET_CLASS, + draw_widget = XtCreateManagedWidget("drawing", drawWidgetClass, vport_widget, draw_args, XtNumber(draw_args)); + XtAddActions(addr_actions, XtNumber(addr_actions)); + + addr_args[0].value = (XtArgVal) page_w; + addr_args[1].value = (XtArgVal) vport_widget; + addr_widget = XtCreateManagedWidget("address", asciiTextWidgetClass, + form_or_top, addr_args, XtNumber(addr_args)); + + XawTextSetInsertionPoint(addr_widget, strlen(addr_default)); + XtTranslations xlats = + XtParseTranslationTable("<Key>Return:addr-go()"); + XtOverrideTranslations(addr_widget, xlats); + + + XtOverrideTranslations( form_or_vport, diff --git a/xdvi.h b/xdvi.h @@ -766,8 +766,8 @@ Boolean drawing_mag = False; Widget top_level = 0; Widget vport_widget, draw_widget, clip_widget; +Widget addr_widget, form_widget; # if BUTTONS -Widget form_widget; int xtra_wid = 0; extern const char default_button_config[]; /* defined in events.c */ Widget panel_widget;