/* * browser.c - unix interface to web browser * * Copyright (C) 2000 Stephen F. White, 2003 J. "MUFTI" Scheurich * * This program 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 (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #ifdef __sun # define BSD_COMP #endif #include #include #include #include #include #include #include #include #include "config.h" #include "swt.h" typedef struct SBrowser { STABLE prefs; char *command; int pureVRML97; int useFork; int useRemote; char *remoteCommand; pid_t pid; } SBrowser; extern XtAppContext TheAppContext; static int console = 2; static int browserLaunch(SBROWSER browser, const char *path, SWND wnd); static int browserRemote(SBROWSER browser, const char *path); static void childInput(XtPointer closure , int *source, XtInputId *id); static int waitForProcess(pid_t pid); static pid_t createProcess(const char *cmdline, SWND wnd); static pid_t createSimpleProcess(const char *cmdline); static int errorToConsole = 1; /*******************/ /* browser preview */ /*******************/ SBROWSER swBrowserInit(STABLE prefs) { SBrowser *browser = malloc(sizeof(SBrowser)); browser->prefs = prefs; browser->command = strdup(swGetPreference(prefs, "PreviewCommand", VRML_BROWSER)); #ifdef VRML_REMOTE browser->useRemote = swGetIntPreference(prefs, "PreviewUseRemote", TRUE); #else browser->useRemote = swGetIntPreference(prefs, "PreviewUseRemote", FALSE); #endif browser->pureVRML97 = swGetIntPreference(prefs, "PreviewPureVRML97", TRUE); browser->useFork = swGetIntPreference(prefs, "PreviewUseFork", TRUE); browser->remoteCommand = strdup(swGetPreference(prefs, "PreviewRemoteCommand", VRML_REMOTE_BROWSER)); browser->pid = 0; errorToConsole = swGetIntPreference(prefs, "PreviewErrorToConsole", FALSE); console = open("/dev/console", O_WRONLY | O_NONBLOCK); if (console == -1) console = 2; return browser; } int swBrowserGetPureVRML97(SBROWSER browser) { return browser->pureVRML97; } int swBrowserGetUseFork(SBROWSER browser) { return browser->useFork; } void swBrowserGetSettings(SBROWSER browser, const char **command, int* pureVRML97, int *useRemote, int *useFork, const char **remoteCommand, const char **application, const char **topic) { *command = browser->command; *pureVRML97 = browser->pureVRML97; *useRemote = browser->useRemote; *useFork = browser->useFork; *remoteCommand = browser->remoteCommand; *application = NULL; *topic = NULL; } void swBrowserSetSettings(SBROWSER browser, const char *command, int pureVRML97, int useRemote, int useFork, const char *remoteCommand, const char *application, const char *topic) { free(browser->command); free(browser->remoteCommand); browser->command = strdup(command); browser->pureVRML97 = pureVRML97; browser->useRemote = useRemote; browser->useFork = useFork; browser->remoteCommand = strdup(remoteCommand); swSetPreference(browser->prefs, "PreviewCommand", browser->command); swSetIntPreference(browser->prefs, "PreviewUseRemote", browser->useRemote); swSetIntPreference(browser->prefs, "PreviewPureVRML97", browser->pureVRML97); swSetIntPreference(browser->prefs, "PreviewUseFork", browser->useFork); swSetPreference(browser->prefs, "PreviewRemoteCommand", browser->remoteCommand); } int swBrowserGetSettingsErrorToConsole(SBROWSER browser) { return errorToConsole; } void swBrowserSetSettingsErrorToConsole(SBROWSER browser, int toConsole) { errorToConsole = toConsole; swSetIntPreference(browser->prefs, "PreviewErrorToConsole", errorToConsole); } void swBrowserSetDefault(SBROWSER browser) { browser->command = strdup(VRML_BROWSER); #ifdef VRML_REMOTE browser->useRemote = TRUE; #else browser->useRemote = FALSE; #endif browser->pureVRML97 = TRUE; browser->useFork = TRUE; browser->remoteCommand = strdup(VRML_REMOTE_BROWSER); browser->pid = 0; } void swBrowserPreview(SBROWSER browser, const char *path, SWND wnd) { if (browser->useRemote) { if (!browserRemote(browser, path)) { browserLaunch(browser, path, wnd); } } else { browserLaunch(browser, path, wnd); } } void swBrowserShutdown(SBROWSER browser) { if (!browser) return; if (browser->pid != 0) { kill(browser->pid, SIGTERM); waitForProcess(browser->pid); } free(browser->command); free(browser->remoteCommand); free(browser); } static int browserLaunch(SBROWSER browser, const char *path, SWND wnd) { char arg[1024]; snprintf(arg, 1023, browser->command, path); if (browser->useFork) { browser->pid = createProcess(arg, wnd); if (browser->pid != 0) return TRUE; else return FALSE; } else return system(arg); } static int browserRemote(SBROWSER browser, const char *path) { char url[1024]; char arg[1024]; pid_t pid; snprintf(url, 1023, "file://%s", path); snprintf(arg, 1023, browser->remoteCommand, url); pid = createSimpleProcess(arg); return waitForProcess(pid); } static pid_t createProcess(const char *cmdline, SWND wnd) { pid_t pid; int fd[2]; const char *argv[4]; if (!cmdline || !wnd) return 0; argv[0] = "sh"; argv[1] = "-c"; argv[2] = cmdline; argv[3] = NULL; if (pipe(fd) != 0) return 0; pid = fork(); switch(pid) { case -1: /* error */ close(fd[0]); close(fd[1]); return 0; case 0: /* child process */ if (fd[1] != STDERR_FILENO) { dup2(fd[1], STDERR_FILENO); /* pipe output is now stderr */ close(fd[1]); /* close old pipe fds */ } close(fd[0]); execv("/bin/sh", (char * const *) argv); swCleanup(); exit(1); /* if we get this far, it's an error */ default: /* parent process */ /* add fd[0] to list of inputs, so we can watch the shell's output */ XtAppAddInput(TheAppContext, fd[0], (XtPointer) XtInputReadMask, childInput, (XtPointer) wnd); /* close(fd[1]); */ break; } return pid; } static pid_t createSimpleProcess(const char *cmdline) { pid_t pid; const char *argv[4]; if (!cmdline) return 0; argv[0] = "sh"; argv[1] = "-c"; argv[2] = cmdline; argv[3] = NULL; pid = fork(); switch(pid) { case -1: /* error */ return 0; case 0: /* child process */ execv("/bin/sh", (char * const *) argv); swCleanup(); exit(1); /* if we get this far, it's an error */ default: /* parent process */ break; } return pid; } static void childInput(XtPointer closure , int *source, XtInputId *id) { char buf[1024]; int bytes; bytes = read(*source, buf, 1023); buf[bytes] = '\0'; if (errorToConsole) mywritestr(console, buf); else swMessageBox((SWND) closure, buf, "Preview", SW_MB_OK, SW_MB_WARNING); } static int waitForProcess(pid_t pid) { int status; for(;;) { if (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) return 0; } else { return WIFEXITED(status) && WEXITSTATUS(status) == 0; } } } void swRemoveFile(const char* filename) { unlink(filename); } /**********/ /* upload */ /**********/ typedef struct SUpload { STABLE prefs; char *commandline; char *htmlTag; char *password; } SUpload; SUPLOAD swUploadInit(STABLE prefs) { SUpload *upload = malloc(sizeof(SUpload)); upload->prefs = prefs; upload->commandline = strdup(swGetPreference(prefs, "UploadCommandLine", "")); upload->htmlTag = strdup(swGetPreference(prefs, "UploadHtmlTags", "")); /* do not read/store password for security reasons */ upload->password = malloc(1024); upload->password[0] = 0; mlock(upload->password, 1024); return upload; } void swUploadGetSettings(SUPLOAD upload, const char **commandline, const char **htmlTag, const char **password) { *commandline = upload->commandline; *htmlTag = upload->htmlTag; *password = upload->password; } void swUploadSetSettings(SUPLOAD upload, const char *commandline, const char *htmlTag, const char *password) { free(upload->commandline); free(upload->htmlTag); upload->commandline = strdup(commandline); upload->htmlTag = strdup(htmlTag); if (password != NULL) mystrncpy_secure(upload->password, password, 1023); else upload->password[0] = 0; swSetPreference(upload->prefs, "UploadCommandLine", upload->commandline); swSetPreference(upload->prefs, "UploadHtmlTags", upload->htmlTag); /* do not read/store password for security reasons */ } int swHasUpload(SUPLOAD upload) { if (strlen(upload->commandline) > 0) return 1; else return 0; } char* swUpload(SUPLOAD upload, char *fileToUpload, SHBROWSER browser, SWND wnd) { static char uploadCommand[1024]; FILE *cmd; int f = -1; int i; int needPassword = 0; static char htmlTags[1024]; char *htmlpath = (char *) malloc(1024); htmlpath[0] = 0; if ((upload->password != NULL) && (strlen(upload->password) != 0)) if (strstr(upload->commandline, "%s") != NULL) if (strstr(strstr(upload->commandline, "%s") + 2, "%s") != NULL) needPassword = 1; /* if commandline has two %s */ mlock(uploadCommand, 1024); if (needPassword) mysnprintf(uploadCommand, 1023, upload->commandline, upload->password, fileToUpload); else mysnprintf(uploadCommand, 1023, upload->commandline, fileToUpload, ""); if ((cmd = popen(uploadCommand, "w")) != NULL) { if ((upload->password != NULL) && (strlen(upload->password) != 0)) fputs(upload->password, cmd); if (pclose(cmd) != 0) { swMessageBox(wnd, "upload failed", "Upload failed", SW_MB_OK, SW_MB_WARNING); htmlpath[0] = 0; return htmlpath; } } swGetTempPath(htmlpath, ".dune_upload_reload", ".html", 1024); f = open(htmlpath, O_WRONLY | O_CREAT, 00666); if (f == -1) { swMessageBox(wnd, "open of html file failed", "Upload reload error", SW_MB_OK, SW_MB_WARNING); htmlpath[0] = 0; return htmlpath; } if ((upload->password != NULL) && (strlen(upload->password) != 0)) mysnprintf(htmlTags, 1023, upload->htmlTag, upload->password); else mysnprintf(htmlTags, 1023, upload->htmlTag, ""); if (write(f, htmlTags, sizeof(htmlTags)) != sizeof(htmlTags)) { swMessageBox(wnd, "open of html file failed", "Upload reload error", SW_MB_OK, SW_MB_WARNING); return htmlpath; } close(f); helpBrowserRemote(browser, htmlpath, wnd); for (i = 0; i < 1024; i++) uploadCommand[i] = (char) 0; munlock(uploadCommand, 1024); return htmlpath; } void swUploadCleanupPasswd(SUPLOAD upload) { int i; for (i = 0; i < strlen(upload->password); i++) upload->password[i] = (char) 0; munlock(upload->password, 1024); } /*****************/ /* help browser */ /*****************/ typedef struct SHelpBrowser { STABLE prefs; char *helpCommand; char *helpUrl; char *vrmlUrl; pid_t pid; } SHelpBrowser; int helpBrowserRemote(SHBROWSER browser, const char *path, SWND wnd); SHBROWSER swHelpBrowserInit(STABLE prefs) { char *dunedocs = NULL; SHelpBrowser *browser = malloc(sizeof(SHelpBrowser)); browser->prefs = prefs; browser->helpCommand = strdup(swGetPreference(prefs,"HelpCommand", WWW_BROWSER)); dunedocs = getenv("DUNEDOCS"); if (dunedocs != NULL) { char *html = "/index.html"; char *url; url = (char *) malloc(strlen(dunedocs) + strlen(html) + 1); strcpy(url, dunedocs); strcat(url, html); browser->helpUrl = strdup(swGetPreference(prefs, "HelpURL", url)); free(url); } else browser->helpUrl = strdup(swGetPreference(prefs, "HelpURL", HELP_URL)); browser->vrmlUrl = strdup(swGetPreference(prefs, "HelpVrmlNodes",VRML_NODES_URL)); browser->pid = 0; return browser; } void swHelpBrowserGetSettings(SHBROWSER browser, const char **helpCommand, const char **helpRemoteCommand, const char **helpUrl, const char **vrmlUrl, const char **application, const char **topic) { *helpCommand = browser->helpCommand; *helpUrl = browser->helpUrl; *vrmlUrl = browser->vrmlUrl; *application = NULL; *topic = NULL; } void swHelpBrowserSetSettings(SHBROWSER browser, const char *helpCommand, const char *helpRemoteCommand, const char *helpUrl, const char *vrmlUrl, const char *application, const char *topic) { free(browser->helpCommand); browser->helpCommand = strdup(helpCommand); free(browser->helpUrl); browser->helpUrl = strdup(helpUrl); free(browser->vrmlUrl); browser->vrmlUrl = strdup(vrmlUrl); swSetPreference(browser->prefs, "HelpCommand", browser->helpCommand); swSetPreference(browser->prefs, "HelpURL", browser->helpUrl); swSetPreference(browser->prefs, "HelpVrmlNodes", browser->vrmlUrl); } void swHelpBrowserHTML(SHBROWSER browser, SWND wnd) { char* path=browser->helpUrl; helpBrowserRemote(browser, path, wnd); } void swHelpBrowserVRML(SHBROWSER browser, const char* selection_string, SWND wnd) { char* path=malloc(strlen(browser->vrmlUrl)+strlen(selection_string)+2); strcpy(path,browser->vrmlUrl); strcat(path,"#"); strcat(path,selection_string); helpBrowserRemote(browser, path , wnd); free(path); } void swHelpBrowserShutdown(SHBROWSER browser) { if (!browser) return; if (browser->pid != 0) { kill(browser->pid, SIGTERM); waitForProcess(browser->pid); } free(browser->helpCommand); free(browser->helpUrl); free(browser->vrmlUrl); free(browser); } int helpBrowserRemote(SHBROWSER browser, const char *path, SWND wnd) { char url[1024]; char arg[1024]; char command[1024]; pid_t pid; int i; snprintf(url, 1023, path); snprintf(arg, 1023, browser->helpCommand, url); pid = createSimpleProcess(arg); /* if failed, search first string (till blank) as command */ if (waitForProcess(pid)==0) { for (i=0;(ihelpCommand)) && (i<1024-3);i++) { command[i]=browser->helpCommand[i]; if ((browser->helpCommand[i] !=' ') && (browser->helpCommand[i+1]==' ')) { i++; break; } } command[i]=0; strcat(command," %s"); mysnprintf(arg, 1024, command, path); browser->pid = createProcess(arg, wnd); } if (browser->pid != 0) { return TRUE; } else { return FALSE; } } /****************/ /* text editor */ /**************/ static int fdpipe_message[2]; static int fdpipe_close[2]; int swCheckRunningProcess(void) { char message[2] = { 'l' , '\0' }; /* check if there is something in the pipe (process ended) */ int byteFromPipe = 0; if (read(fdpipe_message[0],message,1) == 1) byteFromPipe = 1; if (byteFromPipe) { write(fdpipe_close[1], message, 1); /* send notification to other process (otherwise data possibly would die with close of pipe) */ close(fdpipe_close[1]); close(fdpipe_message[0]); } return !byteFromPipe; } int swCreateCheckableProcess(const char *cmdline) { char message[2] = { 'l' , '\0' }; pid_t pid; if (pipe(fdpipe_message) != 0) return -1; if (pipe(fdpipe_close) != 0) { close(fdpipe_message[0]); close(fdpipe_message[1]); return -1; } /* switch to non blocking io for pipe of first message */ fcntl(fdpipe_message[0],F_SETFL,O_NONBLOCK); fcntl(fdpipe_message[1],F_SETFL,O_NONBLOCK); pid = fork(); switch(pid) { case -1: /* error */ close(fdpipe_message[0]); close(fdpipe_message[1]); close(fdpipe_close[0]); close(fdpipe_close[1]); return -1; case 0: /* child process */ close(fdpipe_message[0]); close(fdpipe_close[1]); system(cmdline); /* send message to pipe */ write(fdpipe_message[1],message,1); /* wait for notification */ read(fdpipe_close[0],message,1); close(fdpipe_message[1]); close(fdpipe_close[0]); exit(0); default: /* parent process */ close(fdpipe_message[1]); close(fdpipe_close[0]); break; } return 0; } typedef struct STextedit { STABLE prefs; char *texteditCommand; char *texteditLinenumberOption; int texteditUseExtensionTxt; int texteditAllowPopup; } STextedit; STEXTEDIT swTexteditInit(STABLE prefs) { STextedit *textedit = malloc(sizeof(STextedit)); textedit->prefs = prefs; if (getenv("WINEDITOR") != NULL) { textedit->texteditCommand = strdup(swGetPreference(prefs, "TextEditCommand", getenv("WINEDITOR"))); textedit->texteditLinenumberOption = strdup(swGetPreference(prefs, "TextEditLinenumberOption", "+")); } else { #ifndef MACOSX textedit->texteditCommand = strdup(swGetPreference(prefs, "TextEditCommand", "xterm -e vi")); textedit->texteditLinenumberOption = strdup(swGetPreference(prefs, "TextEditLinenumberOption", "+")); #else /* typical MacOS X users would die if they should use vi, but the normal MacOS X texteditor: "/usr/bin/open /Applications/TextEdit.app" is useless here, cause it can not wait for the completion of editing */ textedit->texteditCommand = strdup(swGetPreference(prefs, "TextEditCommand", "/usr/X11R6/bin/xedit")); textedit->texteditLinenumberOption = strdup(swGetPreference(prefs, "TextEditLinenumberOption", "")); #endif } textedit->texteditUseExtensionTxt = swGetIntPreference(prefs, "TextEditUseExtensionTxt", 1); textedit->texteditAllowPopup = swGetIntPreference(prefs, "TextEditAllowPopup", 1); return textedit; } void swTexteditGetSettingsUseExtensionTxt(STEXTEDIT textedit, int *texteditUseExtensionTxt) { *texteditUseExtensionTxt = textedit->texteditUseExtensionTxt; } void swTexteditGetSettings(STEXTEDIT textedit, const char **texteditCommand, const char **texteditLinenumberOption, int *texteditUseExtensionTxt, int *texteditAllowPopup) { *texteditCommand = textedit->texteditCommand; *texteditLinenumberOption = textedit->texteditLinenumberOption; *texteditUseExtensionTxt = textedit->texteditUseExtensionTxt; /* not yet *texteditAllowPopup = textedit->texteditAllowPopup; */ *texteditAllowPopup = 1; } void swTexteditSetSettings(STEXTEDIT textedit, const char *texteditCommand, const char *texteditLinenumberOption, int texteditUseExtensionTxt, int texteditAllowPopup) { free(textedit->texteditCommand); textedit->texteditCommand = strdup(texteditCommand); free(textedit->texteditLinenumberOption); textedit->texteditLinenumberOption = strdup(texteditLinenumberOption); textedit->texteditUseExtensionTxt = texteditUseExtensionTxt; textedit->texteditAllowPopup = texteditAllowPopup; swSetPreference(textedit->prefs, "TextEditCommand", textedit->texteditCommand); swSetPreference(textedit->prefs, "TextEditLinenumberOption", textedit->texteditLinenumberOption); swSetIntPreference(textedit->prefs, "TextEditUseExtensionTxt", textedit->texteditUseExtensionTxt); swSetIntPreference(textedit->prefs, "TextEditAllowPopup", textedit->texteditAllowPopup); }