/* * a simple urlopener * * 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 3 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, see . */ #include #include #include #include #include #include #include #define NOFORK 0 /* if set we wont fork and execute programs */ #define LEN(arr) ((int) (sizeof (arr) / sizeof (arr)[0])) #define BUFF_SIZE 256 /* size of malloc buffers (max program/extension list length) */ #define ARG_LIMIT 20 /* maximum number of arguments in the programs to open */ char *programs[][2] = { {"default", /* this is the default program */ "/usr/bin/qutebrowser"}, {"jpg,jpeg,png", "/usr/bin/feh"}, {"gif,gifv,webm,mp4,mp3,wav,flac", "/usr/bin/mpv --loop --force-window=yes"}, {"pdf", "/usr/bin/mupdf"}, {"slackspecial", "/home/daniel_j/compiled/waterfox/waterfox"} }; char *forceddomains[][2] = { /* if the domain is listed here, the index into programs[x][] * will be the number, don't add the protocol identifier * the www subdomain is considered a different domain */ {"youtube.com", "2"}, {"www.youtube.com", "2"}, {"youtu.be", "2"}, {"streamable.com", "2"}, {"www.streamable.com", "2"}, {"files.slack.com", "4"} }; int islink(char *url) { /* * terribly check if something has a "protocol://" * if it does, we assume it is a link */ char *p = NULL; if ((p = strstr(url, "://"))) { int s = (p-url); /* * check if the number of characters before the '://' is >0 && <10 && contains a '.' * 10 is a reasonable number plucked out of thin air, RFC 3986 does not state a maximum * we would like to avoid false positives, not that it matters much */ if (s > 0 && s <= 10 && strchr(url, '.') != NULL) // it's probably a link return 1; } return 0; } int getext(char *url) { /* * check if the extension of the url (if exists) is in our array * if it is, return the index on the array, otherwise 0 */ int ret = 0; char *p = NULL; if ((p = strrchr(url, '.')+1)) { for (int i = 0; i < LEN(programs); i++) { char *buff = malloc(BUFF_SIZE); if (buff == NULL) { perror("malloc"); return 0; } strncpy(buff, programs[i][0], BUFF_SIZE-1); char *t = strtok(buff, ","); while (t != NULL) { if (strcmp(t, p) == 0) { ret = i; } t = strtok(NULL, ","); } free(buff); } } return ret; } int checkforceddomains(char *url, int ext) { /* * check if the domain is one we should force to use a program */ /* * checking process: * domain is already probably a url * check if first x characters after protocol identifier is in the list? * return the number specified in the array */ int ret = ext; char *buff = malloc(BUFF_SIZE); char *buff2 = malloc(BUFF_SIZE); if (buff == NULL || buff2 == NULL) { perror("malloc"); goto exitdomaincheck; /* exit right away */ } strncpy(buff, url, BUFF_SIZE-1); char *p = NULL; p = strstr(buff, "://"); if (p == NULL) goto exitdomaincheck; /* exit right away */ p += 3; /* get rid of the '://' */ /* * at this point we know: * there exists '://' * so we can tokenise a new buffer at '/' and _assume_ that is the domain */ strncpy(buff2, p, BUFF_SIZE-1); // wow this is bad char *tmp = strchr(buff2, '/'); if (tmp == NULL) /* nothing specificed after the domain, just exit */ goto exitdomaincheck; *(tmp) = '\0'; /* * now we need to loop through the array and check if there is a number to force * we can reuse our first buffer and char pointer here */ for (int i = 0; i < LEN(forceddomains); i++) { strncpy(buff, forceddomains[i][0], BUFF_SIZE-1); if (strcmp(buff, buff2) == 0) { //printf("domain should be forced as %s\n", buff); ret = atoi(forceddomains[i][1]); goto exitdomaincheck; } } exitdomaincheck: free(buff); free(buff2); return ret; } int main(int argc, char *argv[]) { int link = 0; int ext = 0; // we don't care about children signal(SIGCHLD, SIG_IGN); for (int i = 1; i < argc; i++) { /* * loop through every argument, check if it is a link * if we think it is a link, check if the extension exists in the array * fork, execute */ link = islink(argv[i]); //printf("%s is probably %s link\n", argv[i], (link == 1) ? "a" : "not a"); if (link == 1) { ext = getext(argv[i]); // check if the domain should be forced to a program ext = checkforceddomains(argv[i], ext); //printf("program to run is: \"%s %s\"\n", programs[ext][1], argv[i]); if (NOFORK == 0) { pid_t pid = fork(); if (pid == 0) { // child process, we don't want to ignore signals signal(SIGCHLD, SIG_DFL); /* * we don't want std{out,err} to be associated with the terminal, * but we also don't want to close it to avoid the file descriptors * being re-used potentially leading to problems, so reopen them to /dev/null */ freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stderr); char *args[ARG_LIMIT]; char *buff = malloc(BUFF_SIZE); if (buff == NULL) { perror("malloc"); return -1; } /* * the program we're calling might have arguments, * so we tokenise the string and add each part to an array * that we will use in execvp */ strncpy(buff, programs[ext][1], BUFF_SIZE-1); char *t = strtok(buff, " "); int z = 0; while (t != NULL) { args[z] = t; t = strtok(NULL, " "); z++; } args[z] = argv[i]; args[z+1] = (char *)0; execvp(args[0], args); _exit(1); } else if (pid == -1) { perror("fork"); } } } } return 0; }