diff --git a/misc/uca_qubes.xml b/misc/uca_qubes.xml
index 2e4ad8d..64a3ce9 100644
--- a/misc/uca_qubes.xml
+++ b/misc/uca_qubes.xml
@@ -59,7 +59,7 @@
document-open
- Open in DisposableVM
+ Edit in DisposableVM
1507455559234996-8
/usr/lib/qubes/qvm-actions.sh opendvm %F
@@ -70,3 +70,16 @@
+
+ document-open
+ View in DisposableVM
+ 1507455559234997-9
+ /usr/lib/qubes/qvm-actions.sh viewdvm %F
+
+ *
+
+
+
+
+
+
diff --git a/qubes-rpc/dvm2.h b/qubes-rpc/dvm2.h
index 0e5922c..72b73f7 100644
--- a/qubes-rpc/dvm2.h
+++ b/qubes-rpc/dvm2.h
@@ -1,2 +1,3 @@
#define DVM_FILENAME_SIZE 256
#define DVM_SPOOL "/home/user/.dvmspool"
+#define DVM_VIEW_ONLY_PREFIX "view-only-"
diff --git a/qubes-rpc/gui-fatal.c b/qubes-rpc/gui-fatal.c
index 5900ce5..d50db09 100644
--- a/qubes-rpc/gui-fatal.c
+++ b/qubes-rpc/gui-fatal.c
@@ -10,58 +10,58 @@
static void fix_display(void)
{
- setenv("DISPLAY", ":0", 1);
+ setenv("DISPLAY", ":0", 1);
}
static void produce_message(const char * type, const char *fmt, va_list args)
{
- char *dialog_msg;
- char buf[1024];
- (void) vsnprintf(buf, sizeof(buf), fmt, args);
- if (asprintf(&dialog_msg, "%s: %s: %s (error type: %s)",
- program_invocation_short_name, type, buf, strerror(errno)) < 0) {
- fprintf(stderr, "Failed to allocate memory for error message :(\n");
- return;
- }
- fprintf(stderr, "%s\n", dialog_msg);
- switch (fork()) {
- case -1:
- exit(1); //what else
- case 0:
- if (geteuid() == 0)
- if (setuid(getuid()) != 0)
- perror("setuid failed, calling kdialog/zenity as root");
- fix_display();
+ char *dialog_msg;
+ char buf[1024];
+ (void) vsnprintf(buf, sizeof(buf), fmt, args);
+ if (asprintf(&dialog_msg, "%s: %s: %s (error type: %s)",
+ program_invocation_short_name, type, buf, strerror(errno)) < 0) {
+ fprintf(stderr, "Failed to allocate memory for error message :(\n");
+ return;
+ }
+ fprintf(stderr, "%s\n", dialog_msg);
+ switch (fork()) {
+ case -1:
+ exit(1); //what else
+ case 0:
+ if (geteuid() == 0)
+ if (setuid(getuid()) != 0)
+ perror("setuid failed, calling kdialog/zenity as root");
+ fix_display();
#ifdef USE_KDIALOG
- execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL);
+ execlp("/usr/bin/kdialog", "kdialog", "--sorry", dialog_msg, NULL);
#else
- execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL);
+ execlp("/usr/bin/zenity", "zenity", "--error", "--text", dialog_msg, NULL);
#endif
- exit(1);
- default:;
- }
- free(dialog_msg);
+ exit(1);
+ default:;
+ }
+ free(dialog_msg);
}
void gui_fatal(const char *fmt, ...)
{
- va_list args;
- va_start(args, fmt);
- produce_message("Fatal error", fmt, args);
- va_end(args);
- exit(1);
+ va_list args;
+ va_start(args, fmt);
+ produce_message("Fatal error", fmt, args);
+ va_end(args);
+ exit(1);
}
void qfile_gui_fatal(const char *fmt, va_list args) {
- produce_message("Fatal error", fmt, args);
+ produce_message("Fatal error", fmt, args);
exit(1);
}
void gui_nonfatal(const char *fmt, ...)
{
- va_list args;
- va_start(args, fmt);
- produce_message("Information", fmt, args);
- va_end(args);
+ va_list args;
+ va_start(args, fmt);
+ produce_message("Information", fmt, args);
+ va_end(args);
}
diff --git a/qubes-rpc/qfile-agent.c b/qubes-rpc/qfile-agent.c
index de65788..42a1dfd 100644
--- a/qubes-rpc/qfile-agent.c
+++ b/qubes-rpc/qfile-agent.c
@@ -13,107 +13,107 @@
#include
enum {
- PROGRESS_FLAG_NORMAL,
- PROGRESS_FLAG_INIT,
- PROGRESS_FLAG_DONE
+ PROGRESS_FLAG_NORMAL,
+ PROGRESS_FLAG_INIT,
+ PROGRESS_FLAG_DONE
};
void do_notify_progress(long long total, int flag)
{
- const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE");
- const char *progress_type_env = getenv("PROGRESS_TYPE");
- const char *saved_stdout_env = getenv("SAVED_FD_1");
- int ignore;
- if (!progress_type_env)
- return;
- if (!strcmp(progress_type_env, "console") && du_size_env) {
- char msg[256];
- snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r",
- total / 1024, strtoull(du_size_env, NULL, 0));
- ignore = write(2, msg, strlen(msg));
- if (flag == PROGRESS_FLAG_DONE)
- ignore = write(2, "\n", 1);
- }
- if (!strcmp(progress_type_env, "gui") && saved_stdout_env) {
- char msg[256];
- snprintf(msg, sizeof(msg), "%lld\n", total);
- ignore = write(strtoul(saved_stdout_env, NULL, 0), msg,
- strlen(msg));
- }
- if (ignore < 0) {
- /* silence gcc warning */
- }
+ const char *du_size_env = getenv("FILECOPY_TOTAL_SIZE");
+ const char *progress_type_env = getenv("PROGRESS_TYPE");
+ const char *saved_stdout_env = getenv("SAVED_FD_1");
+ int ignore;
+ if (!progress_type_env)
+ return;
+ if (!strcmp(progress_type_env, "console") && du_size_env) {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "sent %lld/%lld KB\r",
+ total / 1024, strtoull(du_size_env, NULL, 0));
+ ignore = write(2, msg, strlen(msg));
+ if (flag == PROGRESS_FLAG_DONE)
+ ignore = write(2, "\n", 1);
+ }
+ if (!strcmp(progress_type_env, "gui") && saved_stdout_env) {
+ char msg[256];
+ snprintf(msg, sizeof(msg), "%lld\n", total);
+ ignore = write(strtoul(saved_stdout_env, NULL, 0), msg,
+ strlen(msg));
+ }
+ if (ignore < 0) {
+ /* silence gcc warning */
+ }
}
void notify_progress(int size, int flag)
{
- static long long total = 0;
- static long long prev_total = 0;
- total += size;
- if (total > prev_total + PROGRESS_NOTIFY_DELTA
- || (flag != PROGRESS_FLAG_NORMAL)) {
- // check for possible error from qfile-unpacker; if error occured,
- // exit() will be called, so don't bother with current state
- // (notify_progress can be called as callback from copy_file())
- if (flag == PROGRESS_FLAG_NORMAL)
- wait_for_result();
- do_notify_progress(total, flag);
- prev_total = total;
- }
+ static long long total = 0;
+ static long long prev_total = 0;
+ total += size;
+ if (total > prev_total + PROGRESS_NOTIFY_DELTA
+ || (flag != PROGRESS_FLAG_NORMAL)) {
+ // check for possible error from qfile-unpacker; if error occured,
+ // exit() will be called, so don't bother with current state
+ // (notify_progress can be called as callback from copy_file())
+ if (flag == PROGRESS_FLAG_NORMAL)
+ wait_for_result();
+ do_notify_progress(total, flag);
+ prev_total = total;
+ }
}
char *get_abs_path(const char *cwd, const char *pathname)
{
- char *ret;
- if (pathname[0] == '/')
- return strdup(pathname);
- if (asprintf(&ret, "%s/%s", cwd, pathname) < 0)
- return NULL;
- else
- return ret;
+ char *ret;
+ if (pathname[0] == '/')
+ return strdup(pathname);
+ if (asprintf(&ret, "%s/%s", cwd, pathname) < 0)
+ return NULL;
+ else
+ return ret;
}
int main(int argc, char **argv)
{
- int i;
- char *entry;
- char *cwd;
- char *sep;
- int ignore_symlinks = 0;
+ int i;
+ char *entry;
+ char *cwd;
+ char *sep;
+ int ignore_symlinks = 0;
- qfile_pack_init();
- register_error_handler(qfile_gui_fatal);
- register_notify_progress(¬ify_progress);
- notify_progress(0, PROGRESS_FLAG_INIT);
- cwd = getcwd(NULL, 0);
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "--ignore-symlinks")==0) {
- ignore_symlinks = 1;
- continue;
- }
+ qfile_pack_init();
+ register_error_handler(qfile_gui_fatal);
+ register_notify_progress(¬ify_progress);
+ notify_progress(0, PROGRESS_FLAG_INIT);
+ cwd = getcwd(NULL, 0);
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "--ignore-symlinks")==0) {
+ ignore_symlinks = 1;
+ continue;
+ }
- entry = get_abs_path(cwd, argv[i]);
+ entry = get_abs_path(cwd, argv[i]);
- do {
- sep = rindex(entry, '/');
- if (!sep)
- gui_fatal
- ("Internal error: nonabsolute filenames not allowed");
- *sep = 0;
- } while (sep[1] == 0);
- if (entry[0] == 0) {
- if (chdir("/") < 0) {
- gui_fatal("Internal error: chdir(\"/\") failed?!");
- }
- } else if (chdir(entry))
- gui_fatal("chdir to %s", entry);
- do_fs_walk(sep + 1, ignore_symlinks);
- free(entry);
- }
- notify_end_and_wait_for_result();
- notify_progress(0, PROGRESS_FLAG_DONE);
- return 0;
+ do {
+ sep = rindex(entry, '/');
+ if (!sep)
+ gui_fatal
+ ("Internal error: nonabsolute filenames not allowed");
+ *sep = 0;
+ } while (sep[1] == 0);
+ if (entry[0] == 0) {
+ if (chdir("/") < 0) {
+ gui_fatal("Internal error: chdir(\"/\") failed?!");
+ }
+ } else if (chdir(entry))
+ gui_fatal("chdir to %s", entry);
+ do_fs_walk(sep + 1, ignore_symlinks);
+ free(entry);
+ }
+ notify_end_and_wait_for_result();
+ notify_progress(0, PROGRESS_FLAG_DONE);
+ return 0;
}
diff --git a/qubes-rpc/qfile-unpacker.c b/qubes-rpc/qfile-unpacker.c
index 86a9527..817277a 100644
--- a/qubes-rpc/qfile-unpacker.c
+++ b/qubes-rpc/qfile-unpacker.c
@@ -17,81 +17,81 @@
#define INCOMING_DIR_ROOT "/home/user/QubesIncoming"
int prepare_creds_return_uid(const char *username)
{
- const struct passwd *pwd;
- pwd = getpwnam(username);
- if (!pwd) {
- perror("getpwnam");
- exit(1);
- }
- setenv("HOME", pwd->pw_dir, 1);
- setenv("USER", username, 1);
- if (setgid(pwd->pw_gid) < 0)
- gui_fatal("Error setting group permissions");
- if (initgroups(username, pwd->pw_gid) < 0)
- gui_fatal("Error initializing groups");
- if (setfsuid(pwd->pw_uid) < 0)
- gui_fatal("Error setting filesystem level permissions");
- return pwd->pw_uid;
+ const struct passwd *pwd;
+ pwd = getpwnam(username);
+ if (!pwd) {
+ perror("getpwnam");
+ exit(1);
+ }
+ setenv("HOME", pwd->pw_dir, 1);
+ setenv("USER", username, 1);
+ if (setgid(pwd->pw_gid) < 0)
+ gui_fatal("Error setting group permissions");
+ if (initgroups(username, pwd->pw_gid) < 0)
+ gui_fatal("Error initializing groups");
+ if (setfsuid(pwd->pw_uid) < 0)
+ gui_fatal("Error setting filesystem level permissions");
+ return pwd->pw_uid;
}
int main(int argc __attribute((__unused__)), char ** argv __attribute__((__unused__)))
{
- char *incoming_dir;
- int uid, ret;
- pid_t pid;
- const char *remote_domain;
- char *procdir_path;
- int procfs_fd;
+ char *incoming_dir;
+ int uid, ret;
+ pid_t pid;
+ const char *remote_domain;
+ char *procdir_path;
+ int procfs_fd;
- uid = prepare_creds_return_uid("user");
+ uid = prepare_creds_return_uid("user");
- remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
- if (!remote_domain) {
- gui_fatal("Cannot get remote domain name");
- exit(1);
- }
- mkdir(INCOMING_DIR_ROOT, 0700);
- if (asprintf(&incoming_dir, "%s/%s", INCOMING_DIR_ROOT, remote_domain) < 0)
- gui_fatal("Error allocating memory");
- mkdir(incoming_dir, 0700);
- if (chdir(incoming_dir))
- gui_fatal("Error chdir to %s", incoming_dir);
+ remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
+ if (!remote_domain) {
+ gui_fatal("Cannot get remote domain name");
+ exit(1);
+ }
+ mkdir(INCOMING_DIR_ROOT, 0700);
+ if (asprintf(&incoming_dir, "%s/%s", INCOMING_DIR_ROOT, remote_domain) < 0)
+ gui_fatal("Error allocating memory");
+ mkdir(incoming_dir, 0700);
+ if (chdir(incoming_dir))
+ gui_fatal("Error chdir to %s", incoming_dir);
- if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
- gui_fatal("Failed to mount a directory %s", incoming_dir);
+ if (mount(".", ".", NULL, MS_BIND | MS_NODEV | MS_NOEXEC | MS_NOSUID, NULL) < 0)
+ gui_fatal("Failed to mount a directory %s", incoming_dir);
- /* parse the input in unprivileged child process, parent will hold root
- * access to unmount incoming dir */
- switch (pid=fork()) {
- case -1:
- gui_fatal("Failed to create new process");
- case 0:
- if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) {
- gui_fatal("Error allocating memory");
- }
- procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY);
- if (procfs_fd < 0)
- perror("Failed to open /proc");
- else
- set_procfs_fd(procfs_fd);
- free(procdir_path);
+ /* parse the input in unprivileged child process, parent will hold root
+ * access to unmount incoming dir */
+ switch (pid=fork()) {
+ case -1:
+ gui_fatal("Failed to create new process");
+ case 0:
+ if (asprintf(&procdir_path, "/proc/%d/fd", getpid()) < 0) {
+ gui_fatal("Error allocating memory");
+ }
+ procfs_fd = open(procdir_path, O_DIRECTORY | O_RDONLY);
+ if (procfs_fd < 0)
+ perror("Failed to open /proc");
+ else
+ set_procfs_fd(procfs_fd);
+ free(procdir_path);
- if (chroot("."))
- gui_fatal("Error chroot to %s", incoming_dir);
- if (setuid(uid) < 0) {
- /* no kdialog inside chroot */
- perror("setuid");
- exit(1);
- }
- return do_unpack();
- }
- if (waitpid(pid, &ret, 0) < 0) {
- gui_fatal("Failed to wait for child process");
- }
- if (umount2(".", MNT_DETACH) < 0)
- gui_fatal("Cannot umount incoming directory");
- if (!WIFEXITED(ret)) {
- gui_fatal("Child process exited abnormally");
- }
- return WEXITSTATUS(ret);
+ if (chroot("."))
+ gui_fatal("Error chroot to %s", incoming_dir);
+ if (setuid(uid) < 0) {
+ /* no kdialog inside chroot */
+ perror("setuid");
+ exit(1);
+ }
+ return do_unpack();
+ }
+ if (waitpid(pid, &ret, 0) < 0) {
+ gui_fatal("Failed to wait for child process");
+ }
+ if (umount2(".", MNT_DETACH) < 0)
+ gui_fatal("Cannot umount incoming directory");
+ if (!WIFEXITED(ret)) {
+ gui_fatal("Child process exited abnormally");
+ }
+ return WEXITSTATUS(ret);
}
diff --git a/qubes-rpc/qopen-in-vm.c b/qubes-rpc/qopen-in-vm.c
index 43cf854..5e796a5 100644
--- a/qubes-rpc/qopen-in-vm.c
+++ b/qubes-rpc/qopen-in-vm.c
@@ -9,100 +9,124 @@
#include
#include
#include
+#include
#include
#include "dvm2.h"
-void send_file(const char *fname)
+void send_file(const char *fname, int view_only)
{
- const char *base;
- char sendbuf[DVM_FILENAME_SIZE];
- int fd = open(fname, O_RDONLY);
- if (fd < 0)
- gui_fatal("open %s", fname);
- base = rindex(fname, '/');
- if (!base)
- base = fname;
- else
- base++;
- if (strlen(base) >= DVM_FILENAME_SIZE)
- base += strlen(base) - DVM_FILENAME_SIZE + 1;
- strncpy(sendbuf,base,DVM_FILENAME_SIZE - 1); /* fills out with NULs */
- sendbuf[DVM_FILENAME_SIZE - 1] = '\0';
- if (!write_all(1, sendbuf, DVM_FILENAME_SIZE))
- gui_fatal("send filename to dispVM");
- if (!copy_fd_all(1, fd))
- gui_fatal("send file to dispVM");
- close(1);
- close(fd);
+ const char *base;
+ char sendbuf[DVM_FILENAME_SIZE] = {0};
+ size_t sendbuf_size = DVM_FILENAME_SIZE;
+ int fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ gui_fatal("open %s", fname);
+ if (view_only) {
+ strncpy(sendbuf, DVM_VIEW_ONLY_PREFIX, sendbuf_size);
+ sendbuf_size -= strlen(DVM_VIEW_ONLY_PREFIX);
+ }
+ base = rindex(fname, '/');
+ if (!base)
+ base = fname;
+ else
+ base++;
+ if (strlen(base) >= sendbuf_size)
+ base += strlen(base) - sendbuf_size + 1;
+ strncat(sendbuf,base,sendbuf_size - 1); /* fills out with NULs */
+ sendbuf[DVM_FILENAME_SIZE - 1] = '\0';
+ if (!write_all(1, sendbuf, DVM_FILENAME_SIZE))
+ gui_fatal("send filename to dispVM");
+ if (!copy_fd_all(1, fd))
+ gui_fatal("send file to dispVM");
+ close(1);
+ close(fd);
}
int copy_and_return_nonemptiness(int tmpfd)
{
- struct stat st;
- if (!copy_fd_all(tmpfd, 0))
- gui_fatal("receiving file from dispVM");
- if (fstat(tmpfd, &st))
- gui_fatal("fstat");
- close(tmpfd);
+ struct stat st;
+ if (!copy_fd_all(tmpfd, 0))
+ gui_fatal("receiving file from dispVM");
+ if (fstat(tmpfd, &st))
+ gui_fatal("fstat");
+ close(tmpfd);
- return st.st_size > 0;
+ return st.st_size > 0;
}
void recv_file_nowrite(const char *fname)
{
- char *tempfile;
- char *errmsg;
- int tmpfd = -1;
+ char *tempfile;
+ char *errmsg;
+ int tmpfd = -1;
- if (asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX") != -1)
- tmpfd = mkstemp(tempfile);
- if (tmpfd < 0)
- gui_fatal("unable to create any temporary file, aborting");
- if (!copy_and_return_nonemptiness(tmpfd)) {
- unlink(tempfile);
- return;
- }
- if (asprintf(&errmsg,
- "The file %s has been edited in Disposable VM and the modified content has been received, "
- "but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been "
- "saved to %s", fname, tempfile) != -1)
+ if (asprintf(&tempfile, "/tmp/file_edited_in_dvm.XXXXXX") != -1)
+ tmpfd = mkstemp(tempfile);
+ if (tmpfd < 0)
+ gui_fatal("unable to create any temporary file, aborting");
+ if (!copy_and_return_nonemptiness(tmpfd)) {
+ unlink(tempfile);
+ return;
+ }
+ if (asprintf(&errmsg,
+ "The file %s has been edited in Disposable VM and the modified content has been received, "
+ "but this file is in nonwritable directory and thus cannot be modified safely. The edited file has been "
+ "saved to %s", fname, tempfile) != -1)
gui_nonfatal(errmsg);
}
void actually_recv_file(const char *fname, const char *tempfile, int tmpfd)
{
- if (!copy_and_return_nonemptiness(tmpfd)) {
- unlink(tempfile);
- return;
- }
- if (rename(tempfile, fname))
- gui_fatal("rename");
+ if (!copy_and_return_nonemptiness(tmpfd)) {
+ unlink(tempfile);
+ return;
+ }
+ if (rename(tempfile, fname))
+ gui_fatal("rename");
}
void recv_file(const char *fname)
{
- int tmpfd = -1;
- char *tempfile;
- if (asprintf(&tempfile, "%s.XXXXXX", fname) != -1) {
- tmpfd = mkstemp(tempfile);
- }
- if (tmpfd < 0)
- recv_file_nowrite(fname);
- else
- actually_recv_file(fname, tempfile, tmpfd);
-}
-
-void talk_to_daemon(const char *fname)
-{
- send_file(fname);
- recv_file(fname);
+ int tmpfd = -1;
+ char *tempfile;
+ if (asprintf(&tempfile, "%s.XXXXXX", fname) != -1) {
+ tmpfd = mkstemp(tempfile);
+ }
+ if (tmpfd < 0)
+ recv_file_nowrite(fname);
+ else
+ actually_recv_file(fname, tempfile, tmpfd);
}
int main(int argc, char ** argv)
{
- signal(SIGPIPE, SIG_IGN);
- if (argc!=2)
- gui_fatal("OpenInVM - no file given?");
- talk_to_daemon(argv[1]);
- return 0;
+ char *fname;
+ int view_only = 0;
+ int ret;
+ const struct option opts[] = {
+ {"view-only", no_argument, &view_only, 1},
+ {0}
+ };
+
+ while ((ret=getopt_long(argc, argv, "", opts, NULL)) != -1) {
+ if (ret == '?') {
+ exit(2);
+ }
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ if (optind >= argc)
+ gui_fatal("OpenInVM - no file given?");
+ fname = argv[optind];
+ send_file(fname, view_only);
+ if (!view_only) {
+ recv_file(fname);
+ } else {
+ /* discard received data */
+ int null_fd = open("/dev/null", O_WRONLY);
+ copy_fd_all(null_fd, 0);
+ close(null_fd);
+ }
+ return 0;
}
diff --git a/qubes-rpc/qvm-actions.sh b/qubes-rpc/qvm-actions.sh
index 691b3f2..3e4a968 100755
--- a/qubes-rpc/qvm-actions.sh
+++ b/qubes-rpc/qvm-actions.sh
@@ -45,6 +45,12 @@ case "$action" in
qvm-open-in-dvm "$file" | zenity --notification --text "Opening $file in DisposableVM..." --timeout 3 &
done
;;
+ viewdvm)
+ for file in "$@"
+ do
+ qvm-open-in-dvm --view-only "$file" | zenity --notification --text "Opening $file in DisposableVM..." --timeout 3 &
+ done
+ ;;
*)
echo "Unknown action. Aborting..."
exit 1
diff --git a/qubes-rpc/qvm-dvm.desktop b/qubes-rpc/qvm-dvm.desktop
index ba34250..e293b01 100644
--- a/qubes-rpc/qvm-dvm.desktop
+++ b/qubes-rpc/qvm-dvm.desktop
@@ -1,10 +1,15 @@
[Desktop Entry]
-Actions=QvmDvm;
+Actions=QvmDvm;QvmViewDvm
Type=Service
X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles
[Desktop Action QvmDvm]
Exec=/usr/bin/qvm-open-in-dvm %U
Icon=kget
-Name=Open In DisposableVM
+Name=Edit In DisposableVM
+
+[Desktop Action QvmViewDvm]
+Exec=/usr/bin/qvm-open-in-dvm --view-only %U
+Icon=kget
+Name=View In DisposableVM
diff --git a/qubes-rpc/qvm-open-in-dvm b/qubes-rpc/qvm-open-in-dvm
index 84af921..8046858 100755
--- a/qubes-rpc/qvm-open-in-dvm
+++ b/qubes-rpc/qvm-open-in-dvm
@@ -20,10 +20,10 @@
#
#
-if ! [ $# = 1 ] ; then
- echo "Usage: $0 filename"
+if ! [ $# = 1 ] && ! [ $# = 2 ]; then
+ echo "Usage: $0 [--view-only] filename"
exit 1
fi
# shellcheck disable=SC2016
-exec qvm-open-in-vm '$dispvm' "$1"
+exec qvm-open-in-vm '$dispvm' "$@"
diff --git a/qubes-rpc/qvm-open-in-vm b/qubes-rpc/qvm-open-in-vm
index d109e6b..ead9acf 100755
--- a/qubes-rpc/qvm-open-in-vm
+++ b/qubes-rpc/qvm-open-in-vm
@@ -20,16 +20,37 @@
#
#
-if ! [ $# = 2 ] ; then
- echo "Usage: $0 vmname filename"
- exit 1
+usage() {
+ echo "Usage: $0 [--view-only] vmname filename"
+ exit 2
+}
+
+qopen_opts=
+target=
+filename=
+
+while [ $# -gt 0 ]; do
+ if [ "x$1" = "x--view-only" ]; then
+ qopen_opts=--view-only
+ elif [ -z "$target" ]; then
+ target="$1"
+ elif [ -z "$filename" ]; then
+ filename="$1"
+ else
+ usage
+ fi
+ shift
+done
+
+if [ -z "$target" ] || [ -z "$filename" ]; then
+ usage
fi
-case "$2" in
+case "$filename" in
*://*)
- exec /usr/lib/qubes/qrexec-client-vm "$1" qubes.OpenURL /bin/echo "$2"
+ exec /usr/lib/qubes/qrexec-client-vm "$target" qubes.OpenURL /bin/echo "$filename"
;;
*)
- exec /usr/lib/qubes/qrexec-client-vm "$1" qubes.OpenInVM "/usr/lib/qubes/qopen-in-vm" "$2"
+ exec /usr/lib/qubes/qrexec-client-vm "$target" qubes.OpenInVM "/usr/lib/qubes/qopen-in-vm" $qopen_opts "$filename"
;;
esac
diff --git a/qubes-rpc/qvm_dvm_nautilus.py b/qubes-rpc/qvm_dvm_nautilus.py
index a4311a4..7614396 100755
--- a/qubes-rpc/qvm_dvm_nautilus.py
+++ b/qubes-rpc/qvm_dvm_nautilus.py
@@ -17,15 +17,24 @@ class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider):
if not files:
return
- menu_item = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm',
- label='Open In DisposableVM',
+ menu_item1 = Nautilus.MenuItem(name='QubesMenuProvider::OpenInDvm',
+ label='Edit In DisposableVM',
tip='',
icon='')
- menu_item.connect('activate', self.on_menu_item_clicked, files)
- return menu_item,
+ menu_item1.connect('activate', self.on_menu_item_clicked, files)
- def on_menu_item_clicked(self, menu, files):
+ menu_item2 = Nautilus.MenuItem(name='QubesMenuProvider::ViewInDvm',
+ label='View In DisposableVM',
+ tip='',
+ icon='')
+
+ menu_item2.connect('activate',
+ self.on_menu_item_clicked,
+ files, True)
+ return menu_item1, menu_item2,
+
+ def on_menu_item_clicked(self, menu, files, view_only=False):
'''Called when user chooses files though Nautilus context menu.
'''
for file_obj in files:
@@ -38,6 +47,11 @@ class OpenInDvmItemExtension(GObject.GObject, Nautilus.MenuProvider):
# Use subprocess.DEVNULL in python >= 3.3
devnull = open(os.devnull, 'wb')
+ command = ['nohup', '/usr/bin/qvm-open-in-dvm']
+ if view_only:
+ command.append('--view-only')
+ command.append(gio_file.get_path())
# Use Popen instead of subprocess.call to spawn the process
- Popen(['nohup', '/usr/bin/qvm-open-in-dvm', gio_file.get_path()], stdout=devnull, stderr=devnull)
+ Popen(command, stdout=devnull, stderr=devnull)
+ devnull.close()
diff --git a/qubes-rpc/vm-file-editor.c b/qubes-rpc/vm-file-editor.c
index 55594d2..45004e8 100644
--- a/qubes-rpc/vm-file-editor.c
+++ b/qubes-rpc/vm-file-editor.c
@@ -19,217 +19,227 @@ static const char *cleanup_dirname = NULL;
static void cleanup_file(void)
{
- if (cleanup_filename) {
- if (unlink(cleanup_filename) < 0)
- fprintf(stderr, "Failed to remove file at exit\n");
- cleanup_filename = NULL;
- }
- if (cleanup_dirname) {
- if (rmdir(cleanup_dirname) < 0)
- fprintf(stderr, "Failed to remove directory at exit\n");
- cleanup_dirname = NULL;
- }
+ if (cleanup_filename) {
+ if (unlink(cleanup_filename) < 0)
+ fprintf(stderr, "Failed to remove file at exit\n");
+ cleanup_filename = NULL;
+ }
+ if (cleanup_dirname) {
+ if (rmdir(cleanup_dirname) < 0)
+ fprintf(stderr, "Failed to remove directory at exit\n");
+ cleanup_dirname = NULL;
+ }
}
const char *gettime(void)
{
- static char retbuf[60];
- struct timeval tv;
- gettimeofday(&tv, NULL);
- snprintf(retbuf, sizeof(retbuf), "%lld.%06lld",
- (long long) tv.tv_sec, (long long) tv.tv_usec);
- return retbuf;
+ static char retbuf[60];
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ snprintf(retbuf, sizeof(retbuf), "%lld.%06lld",
+ (long long) tv.tv_sec, (long long) tv.tv_usec);
+ return retbuf;
}
static char *get_directory(void)
{
- const char *remote_domain;
- char *dir;
- size_t len;
- char *ret;
+ const char *remote_domain;
+ char *dir;
+ size_t len;
+ char *ret;
- remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
- if (!remote_domain) {
- fprintf(stderr, "Cannot get remote domain name\n");
- exit(1);
- }
- if (!*remote_domain || index(remote_domain, '/'))
- goto fail;
- if (!strcmp(remote_domain, ".") || !strcmp(remote_domain, ".."))
- goto fail;
+ remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
+ if (!remote_domain) {
+ fprintf(stderr, "Cannot get remote domain name\n");
+ exit(1);
+ }
+ if (!*remote_domain || index(remote_domain, '/'))
+ goto fail;
+ if (!strcmp(remote_domain, ".") || !strcmp(remote_domain, ".."))
+ goto fail;
- len = strlen("/tmp/-XXXXXX")+strlen(remote_domain)+1;
- dir = malloc(len);
- if (!dir) {
- fprintf(stderr, "Cannot allocate memory\n");
- exit(1);
- }
- snprintf(dir, len, "/tmp/%s-XXXXXX", remote_domain);
+ len = strlen("/tmp/-XXXXXX")+strlen(remote_domain)+1;
+ dir = malloc(len);
+ if (!dir) {
+ fprintf(stderr, "Cannot allocate memory\n");
+ exit(1);
+ }
+ snprintf(dir, len, "/tmp/%s-XXXXXX", remote_domain);
- ret = mkdtemp(dir);
- if (ret == NULL) {
- perror("mkdtemp");
- exit(1);
- }
- cleanup_dirname = strdup(ret);
- return ret;
+ ret = mkdtemp(dir);
+ if (ret == NULL) {
+ perror("mkdtemp");
+ exit(1);
+ }
+ cleanup_dirname = strdup(ret);
+ return ret;
fail:
- fprintf(stderr, "Invalid remote domain name: %s\n", remote_domain);
- exit(1);
+ fprintf(stderr, "Invalid remote domain name: %s\n", remote_domain);
+ exit(1);
}
-char *get_filename(void)
+char *get_filename(int *view_only)
{
- char buf[DVM_FILENAME_SIZE];
- static char *retname;
- int i;
- char *directory;
- size_t len;
+ char buf[DVM_FILENAME_SIZE];
+ char *fname = buf;
+ static char *retname;
+ int i;
+ char *directory;
+ size_t len;
- directory = get_directory();
- if (!read_all(0, buf, sizeof(buf)))
- exit(1);
- buf[DVM_FILENAME_SIZE-1] = 0;
- if (index(buf, '/')) {
- fprintf(stderr, "filename contains /");
- exit(1);
- }
- for (i=0; buf[i]!=0; i++) {
- // replace some characters with _ (eg mimeopen have problems with some of them)
- if (index(" !?\"#$%^&*()[]<>;`~|", buf[i]))
- buf[i]='_';
- }
- len = strlen(directory)+1+strlen(buf)+1;
- retname = malloc(len);
- if (!retname) {
- fprintf(stderr, "Cannot allocate memory\n");
- exit(1);
- }
- snprintf(retname, len, "%s/%s", directory, buf);
- free(directory);
- return retname;
+ directory = get_directory();
+ if (!read_all(0, buf, sizeof(buf)))
+ exit(1);
+ buf[DVM_FILENAME_SIZE-1] = 0;
+ if (index(buf, '/')) {
+ fprintf(stderr, "filename contains /");
+ exit(1);
+ }
+ for (i=0; buf[i]!=0; i++) {
+ // replace some characters with _ (eg mimeopen have problems with some of them)
+ if (index(" !?\"#$%^&*()[]<>;`~|", buf[i]))
+ buf[i]='_';
+ }
+ if (strncmp(buf, DVM_VIEW_ONLY_PREFIX, strlen(DVM_VIEW_ONLY_PREFIX)) == 0) {
+ *view_only = 1;
+ fname += strlen(DVM_VIEW_ONLY_PREFIX);
+ }
+ len = strlen(directory)+1+strlen(fname)+1;
+ retname = malloc(len);
+ if (!retname) {
+ fprintf(stderr, "Cannot allocate memory\n");
+ exit(1);
+ }
+ snprintf(retname, len, "%s/%s", directory, fname);
+ free(directory);
+ return retname;
}
void copy_file_by_name(const char *filename)
{
- int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600);
- if (fd < 0) {
- perror("open file");
- exit(1);
- }
- /* we now have created a new file, ensure we delete it at the end */
- cleanup_filename = strdup(filename);
- atexit(cleanup_file);
- if (!copy_fd_all(fd, 0))
+ int fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0) {
+ perror("open file");
exit(1);
- close(fd);
+ }
+ /* we now have created a new file, ensure we delete it at the end */
+ cleanup_filename = strdup(filename);
+ atexit(cleanup_file);
+ if (!copy_fd_all(fd, 0))
+ exit(1);
+ close(fd);
}
void send_file_back(const char * filename)
{
- int fd = open(filename, O_RDONLY);
- if (fd < 0) {
- perror("open file");
- exit(1);
- }
- if (!copy_fd_all(1, fd))
- exit(1);
- close(fd);
- close(1);
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("open file");
+ exit(1);
+ }
+ if (!copy_fd_all(1, fd))
+ exit(1);
+ close(fd);
+ close(1);
}
int
main()
{
- struct stat stat_pre, stat_post, session_stat;
- char *filename = get_filename();
- int child, status, log_fd, null_fd;
- FILE *waiter_pidfile;
+ struct stat stat_pre, stat_post, session_stat;
+ int view_only = 0;
+ char *filename = get_filename(&view_only);
+ int child, status, log_fd, null_fd;
+ FILE *waiter_pidfile;
- copy_file_by_name(filename);
- if (stat(filename, &stat_pre)) {
- perror("stat pre");
- exit(1);
- }
+ copy_file_by_name(filename);
+ if (view_only) {
+ // mark file as read-only so applications will signal it to the user
+ chmod(filename, 0400);
+ }
+ if (stat(filename, &stat_pre)) {
+ perror("stat pre");
+ exit(1);
+ }
#ifdef DEBUG
- fprintf(stderr, "time=%s, waiting for qubes-session\n", gettime());
+ fprintf(stderr, "time=%s, waiting for qubes-session\n", gettime());
#endif
- // wait for X server to starts (especially in DispVM)
- if (stat("/tmp/qubes-session-env", &session_stat)) {
- switch (child = fork()) {
- case -1:
- perror("fork");
- exit(1);
- case 0:
- waiter_pidfile = fopen("/tmp/qubes-session-waiter", "a");
- if (waiter_pidfile == NULL) {
- perror("fopen waiter_pidfile");
- exit(1);
- }
- fprintf(waiter_pidfile, "%d\n", getpid());
- fclose(waiter_pidfile);
- // check the second time, to prevent race
- if (stat("/tmp/qubes-session-env", &session_stat)) {
- // wait for qubes-session notify
- pause();
- }
- exit(0);
- default:
- waitpid(child, &status, 0);
- if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
- //propagate exit code from child
- exit(WEXITSTATUS(status));
- }
- }
- }
+ // wait for X server to starts (especially in DispVM)
+ if (stat("/tmp/qubes-session-env", &session_stat)) {
+ switch (child = fork()) {
+ case -1:
+ perror("fork");
+ exit(1);
+ case 0:
+ waiter_pidfile = fopen("/tmp/qubes-session-waiter", "a");
+ if (waiter_pidfile == NULL) {
+ perror("fopen waiter_pidfile");
+ exit(1);
+ }
+ fprintf(waiter_pidfile, "%d\n", getpid());
+ fclose(waiter_pidfile);
+ // check the second time, to prevent race
+ if (stat("/tmp/qubes-session-env", &session_stat)) {
+ // wait for qubes-session notify
+ pause();
+ }
+ exit(0);
+ default:
+ waitpid(child, &status, 0);
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+ //propagate exit code from child
+ exit(WEXITSTATUS(status));
+ }
+ }
+ }
#ifdef DEBUG
- fprintf(stderr, "time=%s, starting editor\n", gettime());
+ fprintf(stderr, "time=%s, starting editor\n", gettime());
#endif
- switch (child = fork()) {
- case -1:
- perror("fork");
- exit(1);
- case 0:
- null_fd = open("/dev/null", O_RDONLY);
- dup2(null_fd, 0);
- close(null_fd);
+ switch (child = fork()) {
+ case -1:
+ perror("fork");
+ exit(1);
+ case 0:
+ null_fd = open("/dev/null", O_RDONLY);
+ dup2(null_fd, 0);
+ close(null_fd);
- log_fd = open("/tmp/mimeopen.log", O_CREAT | O_APPEND, 0666);
- if (log_fd == -1) {
- perror("open /tmp/mimeopen.log");
- exit(1);
- }
- dup2(log_fd, 1);
- close(log_fd);
+ log_fd = open("/tmp/mimeopen.log", O_CREAT | O_APPEND, 0666);
+ if (log_fd == -1) {
+ perror("open /tmp/mimeopen.log");
+ exit(1);
+ }
+ dup2(log_fd, 1);
+ close(log_fd);
- setenv("HOME", USER_HOME, 1);
- setenv("DISPLAY", ":0", 1);
- execl("/usr/bin/qubes-open", "qubes-open", filename, (char*)NULL);
- perror("execl");
- exit(1);
- default:
- waitpid(child, &status, 0);
- if (status != 0) {
- char cmd[512];
+ setenv("HOME", USER_HOME, 1);
+ setenv("DISPLAY", ":0", 1);
+ execl("/usr/bin/qubes-open", "qubes-open", filename, (char*)NULL);
+ perror("execl");
+ exit(1);
+ default:
+ waitpid(child, &status, 0);
+ if (status != 0) {
+ char cmd[512];
#ifdef USE_KDIALOG
- snprintf(cmd, sizeof(cmd),
- "HOME=/home/user DISPLAY=:0 /usr/bin/kdialog --sorry 'Unable to handle mimetype of the requested file (exit status: %d)!' > /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1 /tmp/kdialog.log 2>&1