From 2770c2c85c42020fd7c440b536804a4e70542c0d Mon Sep 17 00:00:00 2001 From: gb10 <gb10> Date: Wed, 6 Oct 2010 10:55:13 +0000 Subject: [PATCH] Added better code for calling a web browser and used this for pfetch-www mode --- blixem_.h | 3 +- blxFetch.c | 238 +++++++++++++------------- seqtoolsWebBrowser.c | 387 +++++++++++++++++++++++++++++++++++++++++++ utilities.h | 6 +- 4 files changed, 516 insertions(+), 118 deletions(-) create mode 100644 seqtoolsWebBrowser.c diff --git a/blixem_.h b/blixem_.h index 130c9ca2..2919f578 100644 --- a/blixem_.h +++ b/blixem_.h @@ -26,7 +26,7 @@ * HISTORY: * Last edited: Aug 26 09:09 2009 (edgrif) * Created: Thu Nov 29 10:59:09 2001 (edgrif) - * CVS info: $Id: blixem_.h,v 1.53 2010-10-05 17:40:20 gb10 Exp $ + * CVS info: $Id: blixem_.h,v 1.54 2010-10-06 10:55:13 gb10 Exp $ *------------------------------------------------------------------- */ #ifndef DEF_BLIXEM_P_H @@ -329,6 +329,7 @@ gboolean blxInitConfig(char *config_file, GError **err GKeyFile* blxGetConfig(void) ; gboolean blxConfigSetPFetchSocketPrefs(char *node, int port) ; gboolean blxConfigGetPFetchSocketPrefs(const char **node, int *port) ; +gboolean blxConfigGetPFetchWWWPrefs(); /* blxGff3Parser.c */ diff --git a/blxFetch.c b/blxFetch.c index 78a784f7..794f1163 100644 --- a/blxFetch.c +++ b/blxFetch.c @@ -38,7 +38,7 @@ * HISTORY: * Last edited: Aug 21 17:34 2009 (edgrif) * Created: Tue Jun 17 16:20:26 2008 (edgrif) - * CVS info: $Id: blxFetch.c,v 1.40 2010-10-05 15:51:21 gb10 Exp $ + * CVS info: $Id: blxFetch.c,v 1.41 2010-10-06 10:55:13 gb10 Exp $ *------------------------------------------------------------------- */ @@ -361,73 +361,73 @@ static void externalCommand (char *command, GtkWidget *blxWindow) } -#if !defined(ACEDB) -/* Find an executable and return its complete pathname. - */ -static int findCommand (char *command, const char **retp) -{ -#if !defined(NO_POPEN) - static char retstr[1025] ; - char *path, file[1025], retval; - int found=0; - - /* Don't use csh - fails if the path is not set in .cshrc * / - if (access(csh, X_OK)) { - messout("Could not find %s", csh); - return 0; - } - if (!(pipe = (FILE *)popen(messprintf("%s -cf \"which %s\"", csh, command), "r"))) { - return 0; - } - - while (!feof(pipe)) - fgets(retval, 1024, pipe); - retval[1024] = 0; - pclose(pipe); - - if (cp = strchr(retval, '\n')) *cp = 0; - if (retp) *retp = retval; - - / * Check if whatever "which" returned is an existing and executable file * / - if (!access(retval, F_OK) && !access(retval, X_OK)) - return 1; - else - return 0; - */ - - path = g_malloc(strlen(g_getenv("PATH"))+1); - /* Don't free 'path' since it changes later on - never mind, - we're only calling it once */ - - strcpy(path, g_getenv("PATH")); - path = strtok(path, ":"); - while (path) { - strcpy(file, path); - strcat(file,"/"); - strcat(file, command); - if (!access(file, F_OK) && !access(file, X_OK)) { - found = 1; - break; - } - - path = strtok(0, ":"); - } - - if (found) { - strcpy(retstr, file); - retval = 1; - } - else { - strcpy(retstr, "Can't find executable in path"); - retval = 0; - } - - if (retp) *retp = retstr; - return retval; - -#endif -} -#endif +//#if !defined(ACEDB) +///* Find an executable and return its complete pathname. +// */ +//static int findCommand (char *command, const char **retp) +//{ +//#if !defined(NO_POPEN) +// static char retstr[1025] ; +// char *path, file[1025], retval; +// int found=0; +// +// /* Don't use csh - fails if the path is not set in .cshrc * / +// if (access(csh, X_OK)) { +// messout("Could not find %s", csh); +// return 0; +// } +// if (!(pipe = (FILE *)popen(messprintf("%s -cf \"which %s\"", csh, command), "r"))) { +// return 0; +// } +// +// while (!feof(pipe)) +// fgets(retval, 1024, pipe); +// retval[1024] = 0; +// pclose(pipe); +// +// if (cp = strchr(retval, '\n')) *cp = 0; +// if (retp) *retp = retval; +// +// / * Check if whatever "which" returned is an existing and executable file * / +// if (!access(retval, F_OK) && !access(retval, X_OK)) +// return 1; +// else +// return 0; +// */ +// +// path = g_malloc(strlen(g_getenv("PATH"))+1); +// /* Don't free 'path' since it changes later on - never mind, +// we're only calling it once */ +// +// strcpy(path, g_getenv("PATH")); +// path = strtok(path, ":"); +// while (path) { +// strcpy(file, path); +// strcat(file,"/"); +// strcat(file, command); +// if (!access(file, F_OK) && !access(file, X_OK)) { +// found = 1; +// break; +// } +// +// path = strtok(0, ":"); +// } +// +// if (found) { +// strcpy(retstr, file); +// retval = 1; +// } +// else { +// strcpy(retstr, "Can't find executable in path"); +// retval = 0; +// } +// +// if (retp) *retp = retstr; +// return retval; +// +//#endif +//} +//#endif /* Display the embl entry for a sequence via pfetch, efetch or whatever. */ @@ -438,6 +438,7 @@ void fetchAndDisplaySequence(char *seqName, GtkWidget *blxWindow) #endif { const char *fetchMode = blxWindowGetFetchMode(blxWindow); + GError *error = NULL; if (!strcmp(fetchMode, BLX_FETCH_PFETCH)) { @@ -457,39 +458,9 @@ void fetchAndDisplaySequence(char *seqName, GtkWidget *blxWindow) } else if (!strcmp(fetchMode, BLX_FETCH_WWW_EFETCH)) { -#ifdef ACEDB - char *command = blxprintf("%s%s", URL, seqName); - graphWebBrowser (command); - g_free(command); -#else - { - const char *browser = g_getenv("BLIXEM_WWW_BROWSER"); - - if (!browser) - { - printf("Looking for WWW browsers ...\n"); - if (!findCommand("netscape", &browser) && - !findCommand("Netscape", &browser) && - !findCommand("Mosaic", &browser) && - !findCommand("mosaic", &browser) && - !findCommand("xmosaic", &browser) && - !findCommand("firefox", &browser) && - !findCommand("Safari", &browser)) - { - g_warning("Couldn't find any WWW browser. Looked for " - "netscape, Netscape, Mosaic, xmosaic, mosaic, firefox & Safari. " - "System message: \"%s\"\n", browser); - return; - } - } - printf("Using WWW browser %s\n", browser); - fflush(stdout); - - char *command = blxprintf("%s %s%s&", browser, URL, seqName); - system(command); - g_free(command); - } -#endif + char *link = blxprintf("%s%s", URL, seqName); + seqtoolsLaunchWebBrowser(link, &error); + g_free(link); } #ifdef ACEDB else if (!strcmp(fetchMode, BLX_FETCH_ACEDB)) @@ -502,15 +473,12 @@ void fetchAndDisplaySequence(char *seqName, GtkWidget *blxWindow) } #endif else - g_critical("Unknown fetchMode: %s", fetchMode); - - if (!URL) { - char *tmpUrl = g_malloc(256); - strcpy(tmpUrl, "http://www.sanger.ac.uk/cgi-bin/seq-query?"); - URL = tmpUrl; + g_critical("Unknown fetchMode: %s", fetchMode); } + reportAndClearIfError(&error, G_LOG_LEVEL_CRITICAL); + return ; } @@ -525,7 +493,7 @@ void blxFindInitialFetchMode(char *fetchMode) { strcpy(fetchMode, BLX_FETCH_PFETCH); } - else if ((URL = g_getenv("BLIXEM_FETCH_WWW"))) + else if (g_getenv("BLIXEM_FETCH_WWW")) { strcpy(fetchMode, BLX_FETCH_WWW_EFETCH); } @@ -1108,6 +1076,7 @@ gboolean blxConfigGetPFetchSocketPrefs(const char **node, int *port) return result ; } +/* Set the preferences for pfetch mode from the config file */ gboolean blxConfigSetPFetchSocketPrefs(char *node, int port) { gboolean result = TRUE ; /* Can't fail. */ @@ -1124,6 +1093,43 @@ gboolean blxConfigSetPFetchSocketPrefs(char *node, int port) return result ; } +/* Set the preferences for pfetch-www mode from the config file */ +gboolean blxConfigGetPFetchWWWPrefs() +{ + gboolean result = TRUE ; /* Can't fail. */ + + /* Try the environment var first */ + URL = g_getenv("BLIXEM_FETCH_WWW"); + + if (!URL) + { + GKeyFile *key_file = blxGetConfig() ; + + if (key_file && g_key_file_has_group(key_file, PFETCH_PROXY_GROUP)) + { + /* Note that this call will create the URL if it doesn't exist but will + * overwrite any existing value. */ + GError *error = NULL; + + char *tmpUrl = g_malloc(256); + tmpUrl = g_key_file_get_string(key_file, PFETCH_PROXY_GROUP, PFETCH_PROXY_LOCATION, &error) ; + reportAndClearIfError(&error, G_LOG_LEVEL_CRITICAL); + + URL = blxprintf("%s?request=", tmpUrl); + g_free(tmpUrl); + } + + if (!URL || *URL == '\0') + { + char *tmpUrl = g_malloc(256); + strcpy(tmpUrl, "http://www.sanger.ac.uk/cgi-bin/seq-query?"); + URL = tmpUrl; + } + } + + + return result ; +} @@ -1838,15 +1844,15 @@ static gboolean setupPfetchMode(PfetchParams *pfetch, const char *fetchMode, con /* Set up the fetch mode. Sets the fetch-mode, and also net_id and port if relevant */ void setupFetchMode(PfetchParams *pfetch, char **fetchMode, const char **net_id, int *port) { - /* First, set the fetch mode */ - if (pfetch) + /* Set up the pfetch and www-pfetch config from the file / env vars etc. We need + * to set them up even if we're not using that mode initially, because the user can + * change the mode */ + blxConfigGetPFetchWWWPrefs(); + + /* Set the fetch mode */ + if (pfetch && blxConfigSetPFetchSocketPrefs(pfetch->net_id, pfetch->port)) { - /* If pfetch struct then this sets fetch mode to pfetch. */ - - if (blxConfigSetPFetchSocketPrefs(pfetch->net_id, pfetch->port)) - { - *fetchMode = BLX_FETCH_PFETCH; - } + *fetchMode = BLX_FETCH_PFETCH; } else { diff --git a/seqtoolsWebBrowser.c b/seqtoolsWebBrowser.c new file mode 100644 index 00000000..3f8d1678 --- /dev/null +++ b/seqtoolsWebBrowser.c @@ -0,0 +1,387 @@ +/* File: seqtoolsWebBrowser.c + * Author: Ed Griffiths (edgrif@sanger.ac.uk) + * Copyright (c) 2006-2010: Genome Research Ltd. + *------------------------------------------------------------------- + * SeqTools is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt + *------------------------------------------------------------------- + * This file is part of the SeqTools sequenceing tools package. + * Code was taken from the ZMap genome database package, + * originated by + * Ed Griffiths (Sanger Institute, UK) edgrif@sanger.ac.uk, + * Roy Storey (Sanger Institute, UK) rds@sanger.ac.uk, + * Malcolm Hinsley (Sanger Institute, UK) mh17@sanger.ac.uk + * adapted for SeqTools by + * Gemma Barson (Sanger Institute, UK) gb10@sanger.ac.uk + * + * Description: Functions to display a url in a web browser. + * + * Exported functions: See utilities.h + *------------------------------------------------------------------- + */ + +#include <SeqTools/utilities.h> +#include <sys/utsname.h> +#include <glib.h> +#include <stdlib.h> +#include <string.h> + +/* Describes various browsers, crude at the moment, we will probably need more options later. */ +typedef struct +{ + char *system ; /* system name as in "uname -s" */ + char *executable ; /* executable name or full path. */ + char *open_command ; /* alternative command to start browser. */ +} BrowserConfigStruct, *BrowserConfig ; + + +static char *findBrowser(BrowserConfig browsers, BrowserConfig *browser_out, GError **error) ; +static void makeBrowserCmd(GString *cmd, BrowserConfig best_browser, char *url) ; +static char *translateURLChars(char *orig_link) ; +gboolean seqtools_g_string_replace(GString *string, char *target, char *source); + + +/* Records information for running a specific browser. The intent here is to add enough + * information to allow us to use stuff like Netscapes "open" subcommand which opens the + * url in an already running netscape if there is one, otherwise in a new netscape. + * + * (see w2/graphgdkremote.c in acedb for some useful stuff.) + * + * (See manpage for more options for "open" on the Mac, e.g. we could specify to always + * use Safari via the -a flag...which would also deal with badly specified urls...) + * + * In the open_command the %U is substituted with the URL. + * + * Note that if open_command is NULL then the executable name is simply combined with + * the URL in the expected way to form the command: "executable URL" + * + * + * Here's one from gnome...this will start in a new window or start a new browser as required. + * /usr/bin/gnome-moz-remote --newwin www.acedb.org + * + * + * */ +#define BROWSER_PATTERN "%U" + +/* List of browsers for different systems, you can have more than one browser for a system. */ +static BrowserConfigStruct browsers_G[] = + { + {"Linux", "iceweasel", "iceweasel -new-window \""BROWSER_PATTERN"\""}, + {"Linux", "firefox", "firefox -browser \""BROWSER_PATTERN"\""}, + {"Linux", "mozilla", "mozilla -remote 'openurl(\""BROWSER_PATTERN"\",new-window)' || mozilla \""BROWSER_PATTERN"\""}, + {"OSF", "netscape", NULL}, + {"Darwin", "/Applications/Safari.app/Contents/MacOS/Safari", "open \""BROWSER_PATTERN"\""}, + {NULL, NULL} /* Terminator record. */ + } ; + + +/* Error handling stuff. */ +static char *domain_G = "SEQTOOLS_WEB" ; +enum {BROWSER_NOT_FOUND, BROWSER_COMMAND_FAILED, BROWSER_UNAME_FAILED, BROWSER_NOT_REGISTERED} ; +static GQuark err_domain_G = 0 ; + + + + +/*! @addtogroup seqtoolsutils + * @{ || + * */ + + +/*! + * Launches a web browser to display the specified link. The browser is chosen + * from an internal list of browsers for different machines. As so much can go + * wrong this function returns FALSE and a GError struct when an error occurs. + * You should call this function like this: + * + * GError *error = NULL ; + * + * if (!(seqtoolsLaunchWebBrowser("www.acedb.org", &error))) + * { || + * printf("Error: %s\n", error->message) ; + * + * g_error_free(error) ; + * } + * + * @param link url to be shown in browser. + * @param error pointer to NULL GError pointer for return of errors. + * @return gboolean TRUE if launch of browser successful. + */ +gboolean seqtoolsLaunchWebBrowser(char *link, GError **error) +{ + gboolean result = FALSE ; + BrowserConfig best_browser = NULL ; + char *browser = NULL ; + + g_assert(link && *link && error && !(*error)) ; + + if (!err_domain_G) + err_domain_G = g_quark_from_string(domain_G) ; + + + /* Check we have a registered browser for this system. */ + browser = findBrowser(browsers_G, &best_browser, error) ; + + + + /* Run the browser in a separate process. */ + if (browser) + { + char *url ; + GString *sys_cmd ; + int sys_rc ; + + /* Translate troublesome chars to their url escape sequences, see translateURLChars() for explanation. */ + url = translateURLChars(link) ; + + sys_cmd = g_string_sized_new(1024) ; /* Should be long enough for most urls. */ + + if (best_browser->open_command) + { + makeBrowserCmd(sys_cmd, best_browser, url) ; + } + else + { + g_string_printf(sys_cmd, "%s \"%s\"", browser, url) ; + } + + /* Make sure browser is run in background by the shell so we do not wait. + * NOTE that because we do not wait for the command to be executed, + * we cannot tell if the command actually worked, only that the shell + * got exec'd */ + g_string_append(sys_cmd, " &") ; + + /* We could do much more to interpret what exactly failed here... */ + if ((sys_rc = system(sys_cmd->str)) == EXIT_SUCCESS) + { + result = TRUE ; + } + else + { + *error = g_error_new(err_domain_G, BROWSER_COMMAND_FAILED, + "Failed to run command \"%s\".", sys_cmd->str) ; + } + + g_string_free(sys_cmd, TRUE) ; + g_free(url) ; + } + + + return result ; +} + + +/*! @} end of seqtoolsutils docs. */ + + + + + + +/* + * Internal functions. + */ + + + + +/* Gets the system name and then finds browsers for that system from our || + * our internal list, if it finds the browser is in the users path then + * returns the path otherwise returns NULL and sets error to give details + * of what went wrong. */ +static char *findBrowser(BrowserConfig browsers_in, BrowserConfig *browser_out, GError **error) +{ + char *browser = NULL ; + struct utsname unamebuf ; + gboolean browser_in_list = FALSE ; + + if (uname(&unamebuf) == -1) + { + *error = g_error_new_literal(err_domain_G, BROWSER_UNAME_FAILED, + "uname() call to find system name failed") ; + } + else + { + BrowserConfig curr_browser = browsers_in ; + + while (curr_browser->system != NULL) + { + if (g_ascii_strcasecmp(curr_browser->system, unamebuf.sysname) == 0) + { + browser_in_list = TRUE ; + + /* Look for the browser in the users path. */ + if ((browser = g_find_program_in_path(curr_browser->executable))) + { + *browser_out = curr_browser ; + + break ; + } + } + + curr_browser++ ; + } + } + + if (!browser) + { + if (browser_in_list) + { + *error = g_error_new(err_domain_G, BROWSER_NOT_FOUND, + "Browser(s) registered with SeqTools for this system (%s)" + " but none found in $PATH or they were not executable.", unamebuf.sysname) ; + } + else + { + *error = g_error_new(err_domain_G, BROWSER_NOT_REGISTERED, + "No browser registered for system %s", unamebuf.sysname) ; + } + } + + return browser ; +} + + +static void makeBrowserCmd(GString *cmd, BrowserConfig best_browser, char *url) +{ + gboolean found ; + + cmd = g_string_append(cmd, best_browser->open_command) ; + + found = seqtools_g_string_replace(cmd, BROWSER_PATTERN, url) ; + + g_assert(found) ; /* Must find at least one pattern. */ + + return ; +} + + +/* URL standards provide escape sequences for all chars which gets round problems + * with them being wrongly interpreted. This function translates chars in the url + * that give us problems for various reasons: + * + * If we use the netscape or Mozilla "OpenURL" remote commands to display links, then sadly the + * syntax for this command is: "OpenURL(URL[,new-window])" and stupid + * netscape/mozilla will think that any "," in the url (i.e. lots of cgi links have + * "," to separate args !) is the "," for its OpenURL command and will then + * usually report a syntax error in the OpenURL command. + * + * To get round this we translate any "," into "%2C" which is the standard + * code for a "," in urls (thanks to Roger Pettet for this)....YUCH. + * + * Some urls have single quotes in them, if you pass the whole string through + * to the shell these get wrongly interpreted by the shell in its normal + * string fashion so we translate them all to "%27". + * + * mh17: for security we need to patch out other shell special characters such as '|', ';', '`'. >,< should be harmless + * Anything that allows a user to run another command is a no-no + * This gets inefficient (perl and php might do this better) + * we can quote the url, but that opens the door to interpretation, better to code metachars as hex. + " + * The returned string should be g_free'd when no longer needed. + * + * mh17: second thoughts: don't use, these rely on quoting: thrid thoughts: no amount of escaped quoting seems to work + */ +static char *translateURLChars(char *orig_link) +{ + char *url = NULL ; + +#if TRANSLATE + + GString *link ; + char *target, *source ; + + link = g_string_new(orig_link) ; + + target = " " ; + source = "%20" ; + seqtools_g_string_replace(link, target, source) ; + + target = ";" ; + source = "%3B" ; + seqtools_g_string_replace(link, target, source) ; + + target = "," ; + source = "%2C" ; + seqtools_g_string_replace(link, target, source) ; + + target = "'" ; + source = "%27" ; + seqtools_g_string_replace(link, target, source) ; + + target = "&" ; + source = "%26" ; + seqtools_g_string_replace(link, target, source) ; + + target = "|" ; + source = "%7C" ; + seqtools_g_string_replace(link, target, source) ; + + target = "`" ; + source = "%60" ; + seqtools_g_string_replace(link, target, source) ; + + url = g_string_free(link, FALSE) ; +#else + url = g_strdup(orig_link); +#endif + return url ; +} + + + + +/*! + * Substitute one string for another in a GString. + * + * You should note that this routine simply uses the existing GString erase/insert + * functions and so may not be incredibly efficient. If the target string was not + * found the GString remains unaltered and FALSE is returned. + * + * @param string A valid GString. + * @param target The string to be replaced. + * @param source The string to be inserted. + * @return TRUE if a string was replaced, FALSE otherwise. + * */ +gboolean seqtools_g_string_replace(GString *string, char *target, char *source) +{ + gboolean result = FALSE ; + int source_len ; + int target_len ; + int target_pos ; + char *template_ptr ; + + + target_len = strlen(target) ; + source_len = strlen(source) ; + + template_ptr = string->str ; + while ((template_ptr = strstr(template_ptr, target))) + { + result = TRUE ; + + target_pos = template_ptr - string->str ; + + string = g_string_erase(string, target_pos, target_len) ; + + string = g_string_insert(string, target_pos, source) ; + + template_ptr = string->str + target_pos + source_len ; /* Shouldn't go off the end. */ + } + + return result ; +} + + diff --git a/utilities.h b/utilities.h index 41d28f1c..46605ab8 100644 --- a/utilities.h +++ b/utilities.h @@ -376,7 +376,6 @@ void drawHighlightBox(GdkDrawable *drawable, const GdkRectangle char* blxprintf(char *formatStr, ...); void setStatusBarShadowStyle(GtkWidget *statusBar, const char *shadowStyle); - void gtk_text_buffer_insert_markup (GtkTextBuffer *buffer, GtkTextIter *iter, const gchar *markup); @@ -393,4 +392,9 @@ void gtk_text_buffer_set_markup_with_tag (GtkTextBuffer *buffer, void gtk_text_buffer_set_markup (GtkTextBuffer *buffer, const gchar *markup); + +/* seqtoolsWebBrowser.c */ +gboolean seqtoolsLaunchWebBrowser(char *link, GError **error); + + #endif /* _utilities_h_included_ */ -- GitLab