diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 39 | ||||
-rw-r--r-- | src/Makefile.in | 608 | ||||
-rw-r--r-- | src/async-io-coroutine.h | 65 | ||||
-rw-r--r-- | src/dropbox-client-util.c | 91 | ||||
-rw-r--r-- | src/dropbox-client-util.h | 39 | ||||
-rw-r--r-- | src/dropbox-client.c | 174 | ||||
-rw-r--r-- | src/dropbox-client.h | 76 | ||||
-rw-r--r-- | src/dropbox-command-client.c | 893 | ||||
-rw-r--r-- | src/dropbox-command-client.h | 113 | ||||
-rw-r--r-- | src/dropbox.c | 60 | ||||
-rw-r--r-- | src/g-util.h | 49 | ||||
-rw-r--r-- | src/nautilus-dropbox-hooks.c | 337 | ||||
-rw-r--r-- | src/nautilus-dropbox-hooks.h | 79 | ||||
-rw-r--r-- | src/nautilus-dropbox.c | 960 | ||||
-rw-r--r-- | src/nautilus-dropbox.h | 69 |
15 files changed, 3652 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..54fb06a --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,39 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_builddir) + +nautilus_extensiondir=$(NAUTILUS_EXTENSION_DIR) + +nautilus_extension_LTLIBRARIES=libnautilus-dropbox.la + +libnautilus_dropbox_la_CFLAGS = \ + -DDATADIR=\"$(datadir)\" \ + -DEMBLEMDIR=\"$(EMBLEM_DIR)\" \ + -Wall \ + $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) \ + $(NAUTILUS_CFLAGS) \ + $(GLIB_CFLAGS) + +if DEBUG +libnautilus_dropbox_la_CFLAGS += -DND_DEBUG +else +libnautilus_dropbox_la_CFLAGS += -DG_DISABLE_ASSERT -DG_DISABLE_CHECKS +endif + +libnautilus_dropbox_la_SOURCES = \ + nautilus-dropbox.c \ + nautilus-dropbox.h \ + nautilus-dropbox-hooks.h \ + nautilus-dropbox-hooks.c \ + dropbox-command-client.h \ + dropbox-command-client.c \ + dropbox-client.c dropbox-client.h \ + g-util.h \ + async-io-coroutine.h \ + dropbox-client-util.c \ + dropbox-client-util.h \ + dropbox.c + +libnautilus_dropbox_la_LDFLAGS = -module -avoid-version +libnautilus_dropbox_la_LIBADD = $(NAUTILUS_LIBS) $(GLIB_LIBS) diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..548e911 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,608 @@ +# Makefile.in generated by automake 1.11 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@DEBUG_TRUE@am__append_1 = -DND_DEBUG +@DEBUG_FALSE@am__append_2 = -DG_DISABLE_ASSERT -DG_DISABLE_CHECKS +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(nautilus_extensiondir)" +LTLIBRARIES = $(nautilus_extension_LTLIBRARIES) +am__DEPENDENCIES_1 = +libnautilus_dropbox_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libnautilus_dropbox_la_OBJECTS = \ + libnautilus_dropbox_la-nautilus-dropbox.lo \ + libnautilus_dropbox_la-nautilus-dropbox-hooks.lo \ + libnautilus_dropbox_la-dropbox-command-client.lo \ + libnautilus_dropbox_la-dropbox-client.lo \ + libnautilus_dropbox_la-dropbox-client-util.lo \ + libnautilus_dropbox_la-dropbox.lo +libnautilus_dropbox_la_OBJECTS = $(am_libnautilus_dropbox_la_OBJECTS) +libnautilus_dropbox_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) \ + $(libnautilus_dropbox_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libnautilus_dropbox_la_SOURCES) +DIST_SOURCES = $(libnautilus_dropbox_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EMBLEM_DIR = @EMBLEM_DIR@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GREP = @GREP@ +HAVE_PKGCONFIG = @HAVE_PKGCONFIG@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +NAUTILUS_CFLAGS = @NAUTILUS_CFLAGS@ +NAUTILUS_EXTENSION_DIR = @NAUTILUS_EXTENSION_DIR@ +NAUTILUS_LIBS = @NAUTILUS_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PYTHON = @PYTHON@ +RANLIB = @RANLIB@ +RST2MAN = @RST2MAN@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_builddir) + +nautilus_extensiondir = $(NAUTILUS_EXTENSION_DIR) +nautilus_extension_LTLIBRARIES = libnautilus-dropbox.la +libnautilus_dropbox_la_CFLAGS = -DDATADIR=\"$(datadir)\" \ + -DEMBLEMDIR=\"$(EMBLEM_DIR)\" -Wall $(WARN_CFLAGS) \ + $(DISABLE_DEPRECATED_CFLAGS) $(NAUTILUS_CFLAGS) $(GLIB_CFLAGS) \ + $(am__append_1) $(am__append_2) +libnautilus_dropbox_la_SOURCES = \ + nautilus-dropbox.c \ + nautilus-dropbox.h \ + nautilus-dropbox-hooks.h \ + nautilus-dropbox-hooks.c \ + dropbox-command-client.h \ + dropbox-command-client.c \ + dropbox-client.c dropbox-client.h \ + g-util.h \ + async-io-coroutine.h \ + dropbox-client-util.c \ + dropbox-client-util.h \ + dropbox.c + +libnautilus_dropbox_la_LDFLAGS = -module -avoid-version +libnautilus_dropbox_la_LIBADD = $(NAUTILUS_LIBS) $(GLIB_LIBS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-nautilus_extensionLTLIBRARIES: $(nautilus_extension_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(nautilus_extensiondir)" || $(MKDIR_P) "$(DESTDIR)$(nautilus_extensiondir)" + @list='$(nautilus_extension_LTLIBRARIES)'; test -n "$(nautilus_extensiondir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(nautilus_extensiondir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(nautilus_extensiondir)"; \ + } + +uninstall-nautilus_extensionLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(nautilus_extension_LTLIBRARIES)'; test -n "$(nautilus_extensiondir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(nautilus_extensiondir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(nautilus_extensiondir)/$$f"; \ + done + +clean-nautilus_extensionLTLIBRARIES: + -test -z "$(nautilus_extension_LTLIBRARIES)" || rm -f $(nautilus_extension_LTLIBRARIES) + @list='$(nautilus_extension_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libnautilus-dropbox.la: $(libnautilus_dropbox_la_OBJECTS) $(libnautilus_dropbox_la_DEPENDENCIES) + $(libnautilus_dropbox_la_LINK) -rpath $(nautilus_extensiondir) $(libnautilus_dropbox_la_OBJECTS) $(libnautilus_dropbox_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnautilus_dropbox_la-dropbox-client-util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnautilus_dropbox_la-dropbox-client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnautilus_dropbox_la-dropbox-command-client.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnautilus_dropbox_la-dropbox.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox-hooks.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +libnautilus_dropbox_la-nautilus-dropbox.lo: nautilus-dropbox.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -MT libnautilus_dropbox_la-nautilus-dropbox.lo -MD -MP -MF $(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox.Tpo -c -o libnautilus_dropbox_la-nautilus-dropbox.lo `test -f 'nautilus-dropbox.c' || echo '$(srcdir)/'`nautilus-dropbox.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox.Tpo $(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nautilus-dropbox.c' object='libnautilus_dropbox_la-nautilus-dropbox.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -c -o libnautilus_dropbox_la-nautilus-dropbox.lo `test -f 'nautilus-dropbox.c' || echo '$(srcdir)/'`nautilus-dropbox.c + +libnautilus_dropbox_la-nautilus-dropbox-hooks.lo: nautilus-dropbox-hooks.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -MT libnautilus_dropbox_la-nautilus-dropbox-hooks.lo -MD -MP -MF $(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox-hooks.Tpo -c -o libnautilus_dropbox_la-nautilus-dropbox-hooks.lo `test -f 'nautilus-dropbox-hooks.c' || echo '$(srcdir)/'`nautilus-dropbox-hooks.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox-hooks.Tpo $(DEPDIR)/libnautilus_dropbox_la-nautilus-dropbox-hooks.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nautilus-dropbox-hooks.c' object='libnautilus_dropbox_la-nautilus-dropbox-hooks.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -c -o libnautilus_dropbox_la-nautilus-dropbox-hooks.lo `test -f 'nautilus-dropbox-hooks.c' || echo '$(srcdir)/'`nautilus-dropbox-hooks.c + +libnautilus_dropbox_la-dropbox-command-client.lo: dropbox-command-client.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -MT libnautilus_dropbox_la-dropbox-command-client.lo -MD -MP -MF $(DEPDIR)/libnautilus_dropbox_la-dropbox-command-client.Tpo -c -o libnautilus_dropbox_la-dropbox-command-client.lo `test -f 'dropbox-command-client.c' || echo '$(srcdir)/'`dropbox-command-client.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnautilus_dropbox_la-dropbox-command-client.Tpo $(DEPDIR)/libnautilus_dropbox_la-dropbox-command-client.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dropbox-command-client.c' object='libnautilus_dropbox_la-dropbox-command-client.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -c -o libnautilus_dropbox_la-dropbox-command-client.lo `test -f 'dropbox-command-client.c' || echo '$(srcdir)/'`dropbox-command-client.c + +libnautilus_dropbox_la-dropbox-client.lo: dropbox-client.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -MT libnautilus_dropbox_la-dropbox-client.lo -MD -MP -MF $(DEPDIR)/libnautilus_dropbox_la-dropbox-client.Tpo -c -o libnautilus_dropbox_la-dropbox-client.lo `test -f 'dropbox-client.c' || echo '$(srcdir)/'`dropbox-client.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnautilus_dropbox_la-dropbox-client.Tpo $(DEPDIR)/libnautilus_dropbox_la-dropbox-client.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dropbox-client.c' object='libnautilus_dropbox_la-dropbox-client.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -c -o libnautilus_dropbox_la-dropbox-client.lo `test -f 'dropbox-client.c' || echo '$(srcdir)/'`dropbox-client.c + +libnautilus_dropbox_la-dropbox-client-util.lo: dropbox-client-util.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -MT libnautilus_dropbox_la-dropbox-client-util.lo -MD -MP -MF $(DEPDIR)/libnautilus_dropbox_la-dropbox-client-util.Tpo -c -o libnautilus_dropbox_la-dropbox-client-util.lo `test -f 'dropbox-client-util.c' || echo '$(srcdir)/'`dropbox-client-util.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnautilus_dropbox_la-dropbox-client-util.Tpo $(DEPDIR)/libnautilus_dropbox_la-dropbox-client-util.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dropbox-client-util.c' object='libnautilus_dropbox_la-dropbox-client-util.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -c -o libnautilus_dropbox_la-dropbox-client-util.lo `test -f 'dropbox-client-util.c' || echo '$(srcdir)/'`dropbox-client-util.c + +libnautilus_dropbox_la-dropbox.lo: dropbox.c +@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -MT libnautilus_dropbox_la-dropbox.lo -MD -MP -MF $(DEPDIR)/libnautilus_dropbox_la-dropbox.Tpo -c -o libnautilus_dropbox_la-dropbox.lo `test -f 'dropbox.c' || echo '$(srcdir)/'`dropbox.c +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnautilus_dropbox_la-dropbox.Tpo $(DEPDIR)/libnautilus_dropbox_la-dropbox.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dropbox.c' object='libnautilus_dropbox_la-dropbox.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnautilus_dropbox_la_CFLAGS) $(CFLAGS) -c -o libnautilus_dropbox_la-dropbox.lo `test -f 'dropbox.c' || echo '$(srcdir)/'`dropbox.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(nautilus_extensiondir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool \ + clean-nautilus_extensionLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-nautilus_extensionLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-nautilus_extensionLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-nautilus_extensionLTLIBRARIES ctags \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man \ + install-nautilus_extensionLTLIBRARIES install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am \ + uninstall-nautilus_extensionLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/async-io-coroutine.h b/src/async-io-coroutine.h new file mode 100644 index 0000000..125d84e --- /dev/null +++ b/src/async-io-coroutine.h @@ -0,0 +1,65 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * async-io-coroutine.h + * Macros to simplify writing coroutines for the glib main loop. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef ASYNC_IO_COROUTINE_H +#define ASYNC_IO_COROUTINE_H + +#include <glib.h> + +G_BEGIN_DECLS + +#define CRBEGIN(pos) switch (pos) { case 0: +#define CREND } return FALSE +#define CRYIELD(pos) do { pos = __LINE__; return TRUE; case __LINE__:;} while (0) +#define CRHALT return FALSE + +#define CRREADLINE(pos, chan, where) \ + while (1) { \ + gchar *__line; \ + gsize __line_length, __newline_pos; \ + GIOStatus __iostat; \ + \ + __iostat = g_io_channel_read_line(chan, &__line, \ + &__line_length, \ + &__newline_pos, NULL); \ + if (__iostat == G_IO_STATUS_AGAIN) { \ + CRYIELD(pos); \ + } \ + else if (__iostat == G_IO_STATUS_NORMAL) { \ + *(__line + __newline_pos) = '\0'; \ + where = __line; \ + break; \ + } \ + else if (__iostat == G_IO_STATUS_EOF || \ + __iostat == G_IO_STATUS_ERROR) { \ + CRHALT; \ + } \ + else { \ + g_assert_not_reached(); \ + CRHALT; \ + } \ + } + +G_END_DECLS + +#endif diff --git a/src/dropbox-client-util.c b/src/dropbox-client-util.c new file mode 100644 index 0000000..2351c81 --- /dev/null +++ b/src/dropbox-client-util.c @@ -0,0 +1,91 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox-client-util.c + * Utility functions for implementing dropbox clients. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <glib.h> + +static gchar chars_not_to_escape[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 34, 127, + -128, -127, -126, -125, -124, -123, -122, -121, -120, -119, + -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, + -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, + -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, + -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, + -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, + -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, + -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, + -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, + -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, + -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, + -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, + -8, -7, -6, -5, -4, -3, -2, -1, 0 +}; + +gchar *dropbox_client_util_sanitize(const gchar *a) { + /* this function escapes teh following utf-8 characters: + * '\\', '\n', '\t' + */ + return g_strescape(a, chars_not_to_escape); +} + +gchar *dropbox_client_util_desanitize(const gchar *a) { + return g_strcompress(a); +} + +gboolean +dropbox_client_util_command_parse_arg(const gchar *line, GHashTable *return_table) { + gchar **argval; + guint len; + gboolean retval; + + argval = g_strsplit(line, "\t", 0); + len = g_strv_length(argval); + + /* debug("parsed: (%d) %s", len, line); */ + + if (len > 1) { + int i; + gchar **vals; + + vals = g_new(gchar *, len); + vals[len - 1] = NULL; + + for (i = 1; argval[i] != NULL; i++) { + vals[i-1] = dropbox_client_util_desanitize(argval[i]); + + } + + g_hash_table_insert(return_table, + dropbox_client_util_desanitize(argval[0]), + vals); + retval = TRUE; + } + else { + retval = FALSE; + } + + g_strfreev(argval); + return retval; +} + diff --git a/src/dropbox-client-util.h b/src/dropbox-client-util.h new file mode 100644 index 0000000..7813a3f --- /dev/null +++ b/src/dropbox-client-util.h @@ -0,0 +1,39 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox-client-util.h + * Header file for dropbox-client-util.c + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef DROPBOX_CLIENT_UTIL_H +#define DROPBOX_CLIENT_UTIL_H + +#include <glib.h> + +G_BEGIN_DECLS + +gchar *dropbox_client_util_sanitize(const gchar *a); +gchar *dropbox_client_util_desanitize(const gchar *a); + +gboolean +dropbox_client_util_command_parse_arg(const gchar *line, GHashTable *return_table); + +G_END_DECLS + +#endif diff --git a/src/dropbox-client.c b/src/dropbox-client.c new file mode 100644 index 0000000..dbd4f77 --- /dev/null +++ b/src/dropbox-client.c @@ -0,0 +1,174 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox-client.c + * Implements connection handling and C interface for interfacing with the Dropbox daemon. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <glib.h> + +#include "g-util.h" +#include "dropbox-command-client.h" +#include "nautilus-dropbox-hooks.h" +#include "dropbox-client.h" + +static void +hook_on_connect(DropboxClient *dc) { + dc->hook_connect_called = TRUE; + + if (dc->command_connect_called) { + debug("client connection"); + g_hook_list_invoke(&(dc->onconnect_hooklist), FALSE); + /* reset flags */ + dc->hook_connect_called = dc->command_connect_called = FALSE; + } +} + +static void +command_on_connect(DropboxClient *dc) { + dc->command_connect_called = TRUE; + + if (dc->hook_connect_called) { + debug("client connection"); + g_hook_list_invoke(&(dc->onconnect_hooklist), FALSE); + /* reset flags */ + dc->hook_connect_called = dc->command_connect_called = FALSE; + } +} + +static void +command_on_disconnect(DropboxClient *dc) { + dc->command_disconnect_called = TRUE; + + if (dc->hook_disconnect_called) { + debug("client disconnect"); + g_hook_list_invoke(&(dc->ondisconnect_hooklist), FALSE); + /* reset flags */ + dc->hook_disconnect_called = dc->command_disconnect_called = FALSE; + } + else { + nautilus_dropbox_hooks_force_reconnect(&(dc->hookserv)); + } +} + +static void +hook_on_disconnect(DropboxClient *dc) { + dc->hook_disconnect_called = TRUE; + + if (dc->command_disconnect_called) { + debug("client disconnect"); + g_hook_list_invoke(&(dc->ondisconnect_hooklist), FALSE); + /* reset flags */ + dc->hook_disconnect_called = dc->command_disconnect_called = FALSE; + } + else { + dropbox_command_client_force_reconnect(&(dc->dcc)); + } +} + +gboolean +dropbox_client_is_connected(DropboxClient *dc) { + return (dropbox_command_client_is_connected(&(dc->dcc)) && + nautilus_dropbox_hooks_is_connected(&(dc->hookserv))); +} + +void +dropbox_client_force_reconnect(DropboxClient *dc) { + if (dropbox_client_is_connected(dc) == TRUE) { + debug("forcing client to reconnect"); + dropbox_command_client_force_reconnect(&(dc->dcc)); + nautilus_dropbox_hooks_force_reconnect(&(dc->hookserv)); + } +} + +/* should only be called once on initialization */ +void +dropbox_client_setup(DropboxClient *dc) { + nautilus_dropbox_hooks_setup(&(dc->hookserv)); + dropbox_command_client_setup(&(dc->dcc)); + + g_hook_list_init(&(dc->ondisconnect_hooklist), sizeof(GHook)); + g_hook_list_init(&(dc->onconnect_hooklist), sizeof(GHook)); + + dc->hook_disconnect_called = dc->command_disconnect_called = FALSE; + dc->hook_connect_called = dc->command_connect_called = FALSE; + + nautilus_dropbox_hooks_add_on_connect_hook(&(dc->hookserv), + (DropboxHookClientConnectHook) + hook_on_connect, dc); + + dropbox_command_client_add_on_connect_hook(&(dc->dcc), + (DropboxCommandClientConnectHook) + command_on_connect, dc); + + nautilus_dropbox_hooks_add_on_disconnect_hook(&(dc->hookserv), + (DropboxHookClientConnectHook) + hook_on_disconnect, dc); + + dropbox_command_client_add_on_disconnect_hook(&(dc->dcc), + (DropboxCommandClientConnectHook) + command_on_disconnect, dc); +} + +/* not thread safe */ +void +dropbox_client_add_on_disconnect_hook(DropboxClient *dc, + DropboxClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(dc->ondisconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(dc->ondisconnect_hooklist), newhook); +} + +/* not thread safe */ +void +dropbox_client_add_on_connect_hook(DropboxClient *dc, + DropboxClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(dc->onconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(dc->onconnect_hooklist), newhook); +} + +/* not thread safe */ +void +dropbox_client_add_connection_attempt_hook(DropboxClient *dc, + DropboxClientConnectionAttemptHook dhcch, + gpointer ud) { + debug("shouldn't be here..."); + + dropbox_command_client_add_connection_attempt_hook(&(dc->dcc), + dhcch, ud); +} + +/* should only be called once on initialization */ +void +dropbox_client_start(DropboxClient *dc) { + debug("starting connections"); + nautilus_dropbox_hooks_start(&(dc->hookserv)); + dropbox_command_client_start(&(dc->dcc)); +} diff --git a/src/dropbox-client.h b/src/dropbox-client.h new file mode 100644 index 0000000..c981c44 --- /dev/null +++ b/src/dropbox-client.h @@ -0,0 +1,76 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox-client.h + * Header file for dropbox-client.c + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef DROPBOX_CLIENT_H +#define DROPBOX_CLIENT_H + +#include <glib.h> +#include "dropbox-command-client.h" +#include "nautilus-dropbox-hooks.h" + +G_BEGIN_DECLS + +typedef struct { + DropboxCommandClient dcc; + NautilusDropboxHookserv hookserv; + GHookList onconnect_hooklist; + GHookList ondisconnect_hooklist; + gboolean hook_connect_called; + gboolean command_connect_called; + gboolean hook_disconnect_called; + gboolean command_disconnect_called; +} DropboxClient; + +typedef void (*DropboxClientConnectionAttemptHook)(guint, gpointer); +typedef GHookFunc DropboxClientConnectHook; + +void +dropbox_client_setup(DropboxClient *dc); + +void +dropbox_client_start(DropboxClient *dc); + +gboolean +dropbox_client_is_connected(DropboxClient *dc); + +void +dropbox_client_force_reconnect(DropboxClient *dc); + +void +dropbox_client_add_on_connect_hook(DropboxClient *dc, + DropboxClientConnectHook dhcch, + gpointer ud); + +void +dropbox_client_add_on_disconnect_hook(DropboxClient *dc, + DropboxClientConnectHook dhcch, + gpointer ud); + +void +dropbox_client_add_connection_attempt_hook(DropboxClient *dc, + DropboxClientConnectionAttemptHook dhcch, + gpointer ud); + +G_END_DECLS + +#endif diff --git a/src/dropbox-command-client.c b/src/dropbox-command-client.c new file mode 100644 index 0000000..bdec125 --- /dev/null +++ b/src/dropbox-command-client.c @@ -0,0 +1,893 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox-command-client.c + * Implements connection handling and C interface for the Dropbox command socket. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include <stdarg.h> +#include <string.h> + +#include <glib.h> + +#include "g-util.h" +#include "dropbox-client-util.h" +#include "dropbox-command-client.h" +#include "nautilus-dropbox.h" +#include "nautilus-dropbox-hooks.h" + +/* TODO: make this asynchronous ;) */ + +/* + this is a tiny hack, necessitated by the fact that + finish_file info command is in nautilus_dropbox, + this can be cleaned up once the file_info_command isn't a special + case anylonger +*/ +gboolean nautilus_dropbox_finish_file_info_command(DropboxFileInfoCommandResponse *); + +typedef struct { + DropboxCommandClient *dcc; + guint connect_attempt; +} ConnectionAttempt; + +typedef struct { + DropboxCommandClientConnectionAttemptHook h; + gpointer ud; +} DropboxCommandClientConnectionAttempt; + +typedef struct { + DropboxGeneralCommand *dgc; + GHashTable *response; +} DropboxGeneralCommandResponse; + +static gboolean +on_connect(DropboxCommandClient *dcc) { + g_hook_list_invoke(&(dcc->onconnect_hooklist), FALSE); + return FALSE; +} + +static gboolean +on_disconnect(DropboxCommandClient *dcc) { + g_hook_list_invoke(&(dcc->ondisconnect_hooklist), FALSE); + return FALSE; +} + +static gboolean +on_connection_attempt(ConnectionAttempt *ca) { + GList *ll; + + for (ll = ca->dcc->ca_hooklist; ll != NULL; ll = g_list_next(ll)) { + DropboxCommandClientConnectionAttempt *dccca = + (DropboxCommandClientConnectionAttempt *)(ll->data); + dccca->h(ca->connect_attempt, dccca->ud); + } + + g_free(ca); + + return FALSE; +} + +static gboolean +receive_args_until_done(GIOChannel *chan, GHashTable *return_table, + GError **err) { + GIOStatus iostat; + GError *tmp_error = NULL; + guint numargs = 0; + + while (1) { + gchar *line; + gsize term_pos; + + /* if we are getting too many args, connection could be malicious */ + if (numargs >= 20) { + g_set_error(err, + g_quark_from_static_string("malicious connection"), + 0, "malicious connection"); + return FALSE; + } + + /* get the string */ + iostat = g_io_channel_read_line(chan, &line, NULL, + &term_pos, &tmp_error); + if (iostat == G_IO_STATUS_ERROR || tmp_error != NULL) { + g_free(line); + if (tmp_error != NULL) { + g_propagate_error(err, tmp_error); + } + return FALSE; + } + else if (iostat == G_IO_STATUS_EOF) { + g_free(line); + g_set_error(err, + g_quark_from_static_string("connection closed"), + 0, "connection closed"); + return FALSE; + } + + *(line+term_pos) = '\0'; + + if (strcmp("done", line) == 0) { + g_free(line); + break; + } + else { + gboolean parse_result; + + parse_result = dropbox_client_util_command_parse_arg(line, return_table); + g_free(line); + + if (FALSE == parse_result) { + g_set_error(err, + g_quark_from_static_string("parse error"), + 0, "parse error"); + return FALSE; + } + } + + numargs += 1; + } + + return TRUE; +} + +static void my_g_hash_table_get_keys_helper(gpointer key, + gpointer value, + GList **ud) { + *ud = g_list_append(*ud, key); +} + +static GList *my_g_hash_table_get_keys(GHashTable *ght) { + GList *list = NULL; + g_hash_table_foreach(ght, (GHFunc) + my_g_hash_table_get_keys_helper, &list); + return list; +} + +/* + sends a command to the dropbox server + returns an hash of the return values + + in theory, this should disconnection errors + but it doesn't matter right now, any error is a sufficient + condition to disconnect +*/ +static GHashTable * +send_command_to_db(GIOChannel *chan, const gchar *command_name, + GHashTable *args, GError **err) { + GError *tmp_error = NULL; + GIOStatus iostat; + gsize bytes_trans; + gchar *line; + + g_assert(chan != NULL); + g_assert(command_name != NULL); + + +#define WRITE_OR_DIE_SANI(s,l) { \ + gchar *sani_s; \ + sani_s = dropbox_client_util_sanitize(s); \ + iostat = g_io_channel_write_chars(chan, sani_s,l, &bytes_trans, \ + &tmp_error); \ + g_free(sani_s); \ + if (iostat == G_IO_STATUS_ERROR || \ + iostat == G_IO_STATUS_AGAIN) { \ + if (tmp_error != NULL) { \ + g_propagate_error(err, tmp_error); \ + } \ + return NULL; \ + } \ + } + +#define WRITE_OR_DIE(s,l) { \ + iostat = g_io_channel_write_chars(chan, s,l, &bytes_trans, \ + &tmp_error); \ + if (iostat == G_IO_STATUS_ERROR || \ + iostat == G_IO_STATUS_AGAIN) { \ + if (tmp_error != NULL) { \ + g_propagate_error(err, tmp_error); \ + } \ + return NULL; \ + } \ + } + + /* send command to server */ + WRITE_OR_DIE_SANI(command_name, -1); + WRITE_OR_DIE("\n", -1); + + if (args != NULL) { + GList *keys, *li; + + /* oh god */ + keys = glib_check_version(2, 14, 0) + ? my_g_hash_table_get_keys(args) + : g_hash_table_get_keys(args); + + for (li = keys; li != NULL; li = g_list_next(li)) { + int i; + gchar **value; + + WRITE_OR_DIE_SANI((gchar *) li->data, -1); + + value = g_hash_table_lookup(args, li->data); + for (i = 0; value[i] != NULL; i++) { + WRITE_OR_DIE("\t", -1); + WRITE_OR_DIE_SANI(value[i], -1); + } + WRITE_OR_DIE("\n", -1); + } + + g_list_free(keys); + } + + WRITE_OR_DIE("done\n", -1); + +#undef WRITE_OR_DIE +#undef WRITE_OR_DIE_SANI + + g_io_channel_flush(chan, &tmp_error); + if (tmp_error != NULL) { + g_propagate_error(err, tmp_error); + return NULL; + } + + /* now we have to read the data */ + iostat = g_io_channel_read_line(chan, &line, NULL, + NULL, &tmp_error); + if (iostat == G_IO_STATUS_ERROR) { + g_assert(line == NULL); + g_propagate_error(err, tmp_error); + return NULL; + } + else if (iostat == G_IO_STATUS_AGAIN) { + g_assert(line == NULL); + g_set_error(err, + g_quark_from_static_string("dropbox command connection timed out"), + 0, + "dropbox command connection timed out"); + return NULL; + } + else if (iostat == G_IO_STATUS_EOF) { + g_assert(line == NULL); + g_set_error(err, + g_quark_from_static_string("dropbox command connection closed"), + 0, + "dropbox command connection closed"); + return NULL; + } + + /* if the response was okay */ + if (strncmp(line, "ok\n", 3) == 0) { + GHashTable *return_table = + g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + + g_free(line); + line = NULL; + + receive_args_until_done(chan, return_table, &tmp_error); + if (tmp_error != NULL) { + g_hash_table_destroy(return_table); + g_propagate_error(err, tmp_error); + return NULL; + } + + return return_table; + } + /* otherwise */ + else { + /* read errors off until we get done */ + do { + g_free(line); + line = NULL; + + /* clear string */ + iostat = g_io_channel_read_line(chan, &line, NULL, + NULL, &tmp_error); + if (iostat == G_IO_STATUS_ERROR) { + g_assert(line == NULL); + g_propagate_error(err, tmp_error); + return NULL; + } + else if (iostat == G_IO_STATUS_AGAIN) { + g_assert(line == NULL); + g_set_error(err, + g_quark_from_static_string("dropbox command connection timed out"), + 0, + "dropbox command connection timed out"); + return NULL; + + } + else if (iostat == G_IO_STATUS_EOF) { + g_assert(line == NULL); + g_set_error(err, + g_quark_from_static_string("dropbox command connection closed"), + 0, + "dropbox command connection closed"); + return NULL; + } + + /* we got our line */ + } while (strncmp(line, "done\n", 5) != 0); + + g_free(line); + return NULL; + } +} + +static void +do_file_info_command(GIOChannel *chan, DropboxFileInfoCommand *dfic, GError **gerr) { + /* we need to send two requests to dropbox: + file status, and folder_tags */ + GError *tmp_gerr = NULL; + DropboxFileInfoCommandResponse *dficr; + GHashTable *file_status_response = NULL, *args, *folder_tag_response = NULL, *emblems_response = NULL; + gchar *filename = NULL; + + { + gchar *filename_un, *uri; + uri = nautilus_file_info_get_uri(dfic->file); + filename_un = uri ? g_filename_from_uri(uri, NULL, NULL): NULL; + g_free(uri); + if (filename_un) { + filename = g_filename_to_utf8(filename_un, -1, NULL, NULL, NULL); + g_free(filename_un); + if (filename == NULL) { + /* oooh, filename wasn't correctly encoded. mark as */ + debug("file wasn't correctly encoded %s", filename_un); + } + } + } + + if (filename == NULL) { + /* We couldn't get the filename. Just return empty. */ + goto exit; + } + + args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + { + gchar **path_arg; + path_arg = g_new(gchar *, 2); + path_arg[0] = g_strdup(filename); + path_arg[1] = NULL; + g_hash_table_insert(args, g_strdup("path"), path_arg); + } + + emblems_response = send_command_to_db(chan, "get_emblems", args, NULL); + if (emblems_response) { + /* Don't need to do the other calls. */ + g_hash_table_unref(args); + goto exit; + } + + /* send status command to server */ + file_status_response = send_command_to_db(chan, "icon_overlay_file_status", + args, &tmp_gerr); + g_hash_table_unref(args); + args = NULL; + if (tmp_gerr != NULL) { + g_free(filename); + g_assert(file_status_response == NULL); + g_propagate_error(gerr, tmp_gerr); + return; + } + + if (nautilus_file_info_is_directory(dfic->file)) { + args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + { + gchar **paths_arg; + paths_arg = g_new(gchar *, 2); + paths_arg[0] = g_strdup(filename); + paths_arg[1] = NULL; + g_hash_table_insert(args, g_strdup("path"), paths_arg); + } + + folder_tag_response = + send_command_to_db(chan, "get_folder_tag", args, &tmp_gerr); + g_hash_table_unref(args); + args = NULL; + if (tmp_gerr != NULL) { + if (file_status_response != NULL) + g_hash_table_destroy(file_status_response); + g_assert(folder_tag_response == NULL); + g_propagate_error(gerr, tmp_gerr); + return; + } + } + + /* great server responded perfectly, + now let's get this request done, + ...in the glib main loop */ +exit: + dficr = g_new0(DropboxFileInfoCommandResponse, 1); + dficr->dfic = dfic; + dficr->folder_tag_response = folder_tag_response; + dficr->file_status_response = file_status_response; + dficr->emblems_response = emblems_response; + g_idle_add((GSourceFunc) nautilus_dropbox_finish_file_info_command, dficr); + + g_free(filename); + + return; +} + +static gboolean +finish_general_command(DropboxGeneralCommandResponse *dgcr) { + if (dgcr->dgc->handler != NULL) { + dgcr->dgc->handler(dgcr->response, dgcr->dgc->handler_ud); + } + + if (dgcr->response != NULL) { + g_hash_table_unref(dgcr->response); + } + + g_free(dgcr->dgc->command_name); + if (dgcr->dgc->command_args != NULL) { + g_hash_table_unref(dgcr->dgc->command_args); + } + g_free(dgcr->dgc); + g_free(dgcr); + + return FALSE; +} + +static void +do_general_command(GIOChannel *chan, DropboxGeneralCommand *dcac, + GError **gerr) { + GError *tmp_gerr = NULL; + GHashTable *response; + + /* send status command to server */ + response = send_command_to_db(chan, dcac->command_name, + dcac->command_args, &tmp_gerr); + if (tmp_gerr != NULL) { + g_assert(response == NULL); + g_propagate_error(gerr, tmp_gerr); + return; + } + + /* great, the server did the command perfectly, + now call the handler with the response */ + { + DropboxGeneralCommandResponse *dgcr = g_new0(DropboxGeneralCommandResponse, 1); + dgcr->dgc = dcac; + dgcr->response = response; + finish_general_command(dgcr); + } + + return; +} + +static gboolean +check_connection(GIOChannel *chan) { + gchar fake_buf[4096]; + gsize bytes_read; + GError *tmp_error = NULL; + GIOFlags flags; + GIOStatus ret, iostat; + + flags = g_io_channel_get_flags(chan); + + /* set non-blocking */ + ret = g_io_channel_set_flags(chan, flags | G_IO_FLAG_NONBLOCK, NULL); + if (ret == G_IO_STATUS_ERROR) { + return FALSE; + } + + iostat = g_io_channel_read_chars(chan, fake_buf, + sizeof(fake_buf), + &bytes_read, &tmp_error); + + ret = g_io_channel_set_flags(chan, flags, NULL); + if (ret == G_IO_STATUS_ERROR) { + return FALSE; + } + + /* this makes us disconnect from bad servers + (those that send us information without us asking for it) */ + return iostat == G_IO_STATUS_AGAIN; +} + +static gpointer +dropbox_command_client_thread(DropboxCommandClient *data); + +static void +end_request(DropboxCommand *dc) { + if ((gpointer (*)(DropboxCommandClient *data)) dc != &dropbox_command_client_thread) { + switch (dc->request_type) { + case GET_FILE_INFO: { + DropboxFileInfoCommand *dfic = (DropboxFileInfoCommand *) dc; + DropboxFileInfoCommandResponse *dficr = g_new0(DropboxFileInfoCommandResponse, 1); + dficr->dfic = dfic; + dficr->file_status_response = NULL; + dficr->emblems_response = NULL; + g_idle_add((GSourceFunc) nautilus_dropbox_finish_file_info_command, dficr); + } + break; + case GENERAL_COMMAND: { + DropboxGeneralCommand *dgc = (DropboxGeneralCommand *) dc; + DropboxGeneralCommandResponse *dgcr = g_new0(DropboxGeneralCommandResponse, 1); + dgcr->dgc = dgc; + dgcr->response = NULL; + finish_general_command(dgcr); + } + break; + default: + g_assert_not_reached(); + break; + } + } +} + + +static gpointer +dropbox_command_client_thread(DropboxCommandClient *dcc) { + struct sockaddr_un addr; + socklen_t addr_len; + int connection_attempts = 1; + + /* intialize address structure */ + addr.sun_family = AF_UNIX; + g_snprintf(addr.sun_path, + sizeof(addr.sun_path), + "%s/.dropbox/command_socket", + g_get_home_dir()); + addr_len = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path); + + while (1) { + GIOChannel *chan = NULL; + GError *gerr = NULL; + int sock; + gboolean failflag = TRUE; + + do { + int flags; + + if (0 > (sock = socket(PF_UNIX, SOCK_STREAM, 0))) { + /* WTF */ + break; + } + + /* set timeout on socket, to protect against + bad servers */ + { + struct timeval tv = {3, 0}; + if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, + &tv, sizeof(struct timeval)) || + 0 > setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, + &tv, sizeof(struct timeval))) { + /* debug("setsockopt failed"); */ + break; + } + } + + /* set native non-blocking, for connect timeout */ + { + if ((flags = fcntl(sock, F_GETFL, 0)) < 0 || + fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { + /* debug("fcntl failed"); */ + break; + } + } + + /* if there was an error we have to try again later */ + if (connect(sock, (struct sockaddr *) &addr, addr_len) < 0) { + if (errno == EINPROGRESS) { + fd_set writers; + struct timeval tv = {1, 0}; + + FD_ZERO(&writers); + FD_SET(sock, &writers); + + /* if nothing was ready after 3 seconds, fail out homie */ + if (select(sock+1, NULL, &writers, NULL, &tv) == 0) { + /* debug("connection timeout"); */ + break; + } + + if (connect(sock, (struct sockaddr *) &addr, addr_len) < 0) { + /* debug("couldn't connect to command server after 1 second"); */ + break; + } + } + /* errno != EINPROGRESS */ + else { + /* debug("bad connection"); */ + break; + } + } + + /* set back to blocking */ + if (fcntl(sock, F_SETFL, flags) < 0) { + /* debug("fcntl2 failed"); */ + break; + } + + failflag = FALSE; + } while (0); + + if (failflag) { + ConnectionAttempt *ca = g_new(ConnectionAttempt, 1); + ca->dcc = dcc; + ca->connect_attempt = connection_attempts; + g_idle_add((GSourceFunc) on_connection_attempt, ca); + if (sock >= 0) { + close(sock); + } + g_usleep(G_USEC_PER_SEC); + connection_attempts++; + continue; + } + else { + connection_attempts = 0; + } + + /* connected */ + debug("command client connected"); + + chan = g_io_channel_unix_new(sock); + g_io_channel_set_close_on_unref(chan, TRUE); + g_io_channel_set_line_term(chan, "\n", -1); + +#define SET_CONNECTED_STATE(s) { \ + g_mutex_lock(dcc->command_connected_mutex); \ + dcc->command_connected = s; \ + g_mutex_unlock(dcc->command_connected_mutex); \ + } + + SET_CONNECTED_STATE(TRUE); + + g_idle_add((GSourceFunc) on_connect, dcc); + + while (1) { + DropboxCommand *dc; + + while (1) { + GTimeVal gtv; + + g_get_current_time(>v); + g_time_val_add(>v, G_USEC_PER_SEC / 10); + /* get a request from nautilus */ + dc = g_async_queue_timed_pop(dcc->command_queue, >v); + if (dc != NULL) { + break; + } + else { + if (check_connection(chan) == FALSE) { + goto BADCONNECTION; + } + } + } + + /* this pointer should be unique */ + if ((gpointer (*)(DropboxCommandClient *data)) dc == &dropbox_command_client_thread) { + debug("got a reset request"); + goto BADCONNECTION; + } + + switch (dc->request_type) { + case GET_FILE_INFO: { + debug("doing file info command"); + do_file_info_command(chan, (DropboxFileInfoCommand *) dc, &gerr); + } + break; + case GENERAL_COMMAND: { + debug("doing general command"); + do_general_command(chan, (DropboxGeneralCommand *) dc, &gerr); + } + break; + default: + g_assert_not_reached(); + break; + } + + debug("done."); + + if (gerr != NULL) { + // debug("COMMAND ERROR*****************************"); + /* mark this request as never to be completed */ + end_request(dc); + + debug("command error: %s", gerr->message); + + g_error_free(gerr); + BADCONNECTION: + /* grab all the rest of the data off the async queue and mark it + never to be completed, who knows how long we'll be disconnected */ + while ((dc = g_async_queue_try_pop(dcc->command_queue)) != NULL) { + end_request(dc); + } + + g_io_channel_unref(chan); + + SET_CONNECTED_STATE(FALSE); + + /* call the disconnect handler */ + g_idle_add((GSourceFunc) on_disconnect, dcc); + + break; + } + } + +#undef SET_CONNECTED_STATE + } + + return NULL; +} + +/* thread safe */ +gboolean +dropbox_command_client_is_connected(DropboxCommandClient *dcc) { + gboolean command_connected; + + g_mutex_lock(dcc->command_connected_mutex); + command_connected = dcc->command_connected; + g_mutex_unlock(dcc->command_connected_mutex); + + return command_connected; +} + +/* thread safe */ +void dropbox_command_client_force_reconnect(DropboxCommandClient *dcc) { + if (dropbox_command_client_is_connected(dcc) == TRUE) { + debug("forcing command to reconnect"); + dropbox_command_client_request(dcc, (DropboxCommand *) &dropbox_command_client_thread); + } +} + +/* thread safe */ +void +dropbox_command_client_request(DropboxCommandClient *dcc, DropboxCommand *dc) { + g_async_queue_push(dcc->command_queue, dc); +} + +/* should only be called once on initialization */ +void +dropbox_command_client_setup(DropboxCommandClient *dcc) { + dcc->command_queue = g_async_queue_new(); + dcc->command_connected_mutex = g_mutex_new(); + dcc->command_connected = FALSE; + dcc->ca_hooklist = NULL; + + g_hook_list_init(&(dcc->ondisconnect_hooklist), sizeof(GHook)); + g_hook_list_init(&(dcc->onconnect_hooklist), sizeof(GHook)); +} + +void +dropbox_command_client_add_on_disconnect_hook(DropboxCommandClient *dcc, + DropboxCommandClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(dcc->ondisconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(dcc->ondisconnect_hooklist), newhook); +} + +void +dropbox_command_client_add_on_connect_hook(DropboxCommandClient *dcc, + DropboxCommandClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(dcc->onconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(dcc->onconnect_hooklist), newhook); +} + +void +dropbox_command_client_add_connection_attempt_hook(DropboxCommandClient *dcc, + DropboxCommandClientConnectionAttemptHook dhcch, + gpointer ud) { + DropboxCommandClientConnectionAttempt *newhook; + + debug("shouldn't be here..."); + + newhook = g_new(DropboxCommandClientConnectionAttempt, 1); + newhook->h = dhcch; + newhook->ud = ud; + + dcc->ca_hooklist = g_list_append(dcc->ca_hooklist, newhook); +} + +/* should only be called once on initialization */ +void +dropbox_command_client_start(DropboxCommandClient *dcc) { + /* setup the connect to the command server */ + debug("starting command thread"); + g_thread_create((gpointer (*)(gpointer data)) dropbox_command_client_thread, + dcc, FALSE, NULL); +} + +/* thread safe */ +void dropbox_command_client_send_simple_command(DropboxCommandClient *dcc, + const char *command) { + DropboxGeneralCommand *dgc; + + dgc = g_new(DropboxGeneralCommand, 1); + + dgc->dc.request_type = GENERAL_COMMAND; + dgc->command_name = g_strdup(command); + dgc->command_args = NULL; + dgc->handler = NULL; + dgc->handler_ud = NULL; + + dropbox_command_client_request(dcc, (DropboxCommand *) dgc); +} + +/* thread safe */ +/* this is the C API, there is another send_command_to_db + that is more the actual over the wire command */ +void dropbox_command_client_send_command(DropboxCommandClient *dcc, + NautilusDropboxCommandResponseHandler h, + gpointer ud, + const char *command, ...) { + va_list ap; + DropboxGeneralCommand *dgc; + gchar *na; + va_start(ap, command); + + dgc = g_new(DropboxGeneralCommand, 1); + dgc->dc.request_type = GENERAL_COMMAND; + dgc->command_name = g_strdup(command); + dgc->command_args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + /* + * NB: The handler is called in the DropboxCommandClient Thread. If you need + * it in the main thread you must call g_idle_add in the callback. + */ + dgc->handler = h; + dgc->handler_ud = ud; + + while ((na = va_arg(ap, char *)) != NULL) { + gchar **is_active_arg; + + is_active_arg = g_new(gchar *, 2); + + g_hash_table_insert(dgc->command_args, + g_strdup(na), is_active_arg); + + is_active_arg[0] = g_strdup(va_arg(ap, char *)); + is_active_arg[1] = NULL; + } + va_end(ap); + + dropbox_command_client_request(dcc, (DropboxCommand *) dgc); +} diff --git a/src/dropbox-command-client.h b/src/dropbox-command-client.h new file mode 100644 index 0000000..c1ea3bc --- /dev/null +++ b/src/dropbox-command-client.h @@ -0,0 +1,113 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox-command-client.h + * Header file for nautilus-dropbox-command.c + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef DROPBOX_COMMAND_CLIENT_H +#define DROPBOX_COMMAND_CLIENT_H + +#include <libnautilus-extension/nautilus-info-provider.h> +#include <libnautilus-extension/nautilus-file-info.h> + +G_BEGIN_DECLS + +/* command structs */ +typedef enum {GET_FILE_INFO, GENERAL_COMMAND} NautilusDropboxRequestType; + +typedef struct { + NautilusDropboxRequestType request_type; +} DropboxCommand; + +typedef struct { + DropboxCommand dc; + NautilusInfoProvider *provider; + GClosure *update_complete; + NautilusFileInfo *file; + gboolean cancelled; +} DropboxFileInfoCommand; + +typedef struct { + DropboxFileInfoCommand *dfic; + GHashTable *file_status_response; + GHashTable *folder_tag_response; + GHashTable *emblems_response; +} DropboxFileInfoCommandResponse; + +typedef void (*NautilusDropboxCommandResponseHandler)(GHashTable *, gpointer); + +typedef struct { + DropboxCommand dc; + gchar *command_name; + GHashTable *command_args; + NautilusDropboxCommandResponseHandler handler; + gpointer handler_ud; +} DropboxGeneralCommand; + +typedef void (*DropboxCommandClientConnectionAttemptHook)(guint, gpointer); +typedef GHookFunc DropboxCommandClientConnectHook; + +typedef struct { + GMutex *command_connected_mutex; + gboolean command_connected; + GAsyncQueue *command_queue; + GList *ca_hooklist; + GHookList onconnect_hooklist; + GHookList ondisconnect_hooklist; +} DropboxCommandClient; + +gboolean dropbox_command_client_is_connected(DropboxCommandClient *dcc); + +void dropbox_command_client_force_reconnect(DropboxCommandClient *dcc); + +void +dropbox_command_client_request(DropboxCommandClient *dcc, DropboxCommand *dc); + +void +dropbox_command_client_setup(DropboxCommandClient *dcc); + +void +dropbox_command_client_start(DropboxCommandClient *dcc); + +void dropbox_command_client_send_simple_command(DropboxCommandClient *dcc, + const char *command); + +void dropbox_command_client_send_command(DropboxCommandClient *dcc, + NautilusDropboxCommandResponseHandler h, + gpointer ud, + const char *command, ...); +void +dropbox_command_client_add_on_connect_hook(DropboxCommandClient *dcc, + DropboxCommandClientConnectHook dhcch, + gpointer ud); + +void +dropbox_command_client_add_on_disconnect_hook(DropboxCommandClient *dcc, + DropboxCommandClientConnectHook dhcch, + gpointer ud); + +void +dropbox_command_client_add_connection_attempt_hook(DropboxCommandClient *dcc, + DropboxCommandClientConnectionAttemptHook dhcch, + gpointer ud); + +G_END_DECLS + +#endif diff --git a/src/dropbox.c b/src/dropbox.c new file mode 100644 index 0000000..0d59559 --- /dev/null +++ b/src/dropbox.c @@ -0,0 +1,60 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * dropbox.c + * Nautilus module registering functions for the Dropbox extension. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib-object.h> + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#include "nautilus-dropbox.h" + +static GType type_list[1]; + +void +nautilus_module_initialize (GTypeModule *module) { + g_print ("Initializing %s\n", PACKAGE_STRING); + + nautilus_dropbox_register_type (module); + type_list[0] = NAUTILUS_TYPE_DROPBOX; + + dropbox_use_nautilus_submenu_workaround + = (NAUTILUS_VERSION_MAJOR < 2 || + (NAUTILUS_VERSION_MAJOR == 2 && NAUTILUS_VERSION_MINOR <= 22)); + dropbox_use_operation_in_progress_workaround = TRUE; +} + +void +nautilus_module_shutdown (void) { + g_print ("Shutting down dropbox extension\n"); +} + +void +nautilus_module_list_types (const GType **types, + int *num_types) { + *types = type_list; + *num_types = G_N_ELEMENTS (type_list); +} diff --git a/src/g-util.h b/src/g-util.h new file mode 100644 index 0000000..73d3d81 --- /dev/null +++ b/src/g-util.h @@ -0,0 +1,49 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * g-util.h + * Helper macros. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef G_UTIL_H +#define G_UTIL_H + +#include <glib.h> +#include <glib/gprintf.h> + +G_BEGIN_DECLS + +#ifdef ND_DEBUG + +#define debug_enter() {g_print("Entering "); g_print(__FUNCTION__); g_printf("\n");} +#define debug(format, ...) {g_print(__FUNCTION__); g_print(": "); \ + g_printf(format, ## __VA_ARGS__); g_print("\n");} +#define debug_return(v) {g_print("Exiting "); g_print(__FUNCTION__); g_printf("\n"); return v;} + +#else + +#define debug_enter() do {} while(0) +#define debug(format, ...) do {} while(0) +#define debug_return(v) do {} while(0) + +#endif + +G_END_DECLS + +#endif diff --git a/src/nautilus-dropbox-hooks.c b/src/nautilus-dropbox-hooks.c new file mode 100644 index 0000000..f37dbbb --- /dev/null +++ b/src/nautilus-dropbox-hooks.c @@ -0,0 +1,337 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * nautilus-dropbox-hooks.c + * Implements connection handling and C interface for the Dropbox hook socket. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include <string.h> + +#include <glib.h> + +#include "g-util.h" +#include "async-io-coroutine.h" +#include "dropbox-client-util.h" +#include "nautilus-dropbox-hooks.h" + +typedef struct { + DropboxUpdateHook hook; + gpointer ud; +} HookData; + +static gboolean +try_to_connect(NautilusDropboxHookserv *hookserv); + +static gboolean +handle_hook_server_input(GIOChannel *chan, + GIOCondition cond, + NautilusDropboxHookserv *hookserv) { + /*debug_enter(); */ + + /* we have some sweet macros defined that allow us to write this + async event handler like a microthread yeahh, watch out for context */ + CRBEGIN(hookserv->hhsi.line); + while (1) { + hookserv->hhsi.command_args = + g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + hookserv->hhsi.numargs = 0; + + /* read the command name */ + { + gchar *line; + CRREADLINE(hookserv->hhsi.line, chan, line); + hookserv->hhsi.command_name = dropbox_client_util_desanitize(line); + g_free(line); + } + + /*debug("got a hook name: %s", hookserv->hhsi.command_name); */ + + /* now read each arg line (until a certain limit) until we receive "done" */ + while (1) { + gchar *line; + + /* if too many arguments, this connection seems malicious */ + if (hookserv->hhsi.numargs >= 20) { + CRHALT; + } + + CRREADLINE(hookserv->hhsi.line, chan, line); + + if (strcmp("done", line) == 0) { + g_free(line); + break; + } + else { + gboolean parse_result; + + parse_result = + dropbox_client_util_command_parse_arg(line, + hookserv->hhsi.command_args); + g_free(line); + + if (FALSE == parse_result) { + debug("bad parse"); + CRHALT; + } + } + + hookserv->hhsi.numargs += 1; + } + + { + HookData *hd; + hd = (HookData *) + g_hash_table_lookup(hookserv->dispatch_table, + hookserv->hhsi.command_name); + if (hd != NULL) { + (hd->hook)(hookserv->hhsi.command_args, hd->ud); + } + } + + g_free(hookserv->hhsi.command_name); + g_hash_table_unref(hookserv->hhsi.command_args); + hookserv->hhsi.command_name = NULL; + hookserv->hhsi.command_args = NULL; + } + CREND; +} + +static void +watch_killer(NautilusDropboxHookserv *hookserv) { + debug("hook client disconnected"); + + hookserv->connected = FALSE; + + g_hook_list_invoke(&(hookserv->ondisconnect_hooklist), FALSE); + + /* we basically just have to free the memory allocated in the + handle_hook_server_init ctx */ + if (hookserv->hhsi.command_name != NULL) { + g_free(hookserv->hhsi.command_name); + hookserv->hhsi.command_name = NULL; + } + + if (hookserv->hhsi.command_args != NULL) { + g_hash_table_unref(hookserv->hhsi.command_args); + hookserv->hhsi.command_args = NULL; + } + + g_io_channel_unref(hookserv->chan); + hookserv->chan = NULL; + hookserv->event_source = 0; + hookserv->socket = 0; + + /* lol we also have to start a new connection */ + try_to_connect(hookserv); +} + +static gboolean +try_to_connect(NautilusDropboxHookserv *hookserv) { + /* create socket */ + hookserv->socket = socket(PF_UNIX, SOCK_STREAM, 0); + + /* set native non-blocking, for connect timeout */ + { + unsigned int flags; + + if ((flags = fcntl(hookserv->socket, F_GETFL, 0)) < 0) { + goto FAIL_CLEANUP; + } + + if (fcntl(hookserv->socket, F_SETFL, flags | O_NONBLOCK) < 0) { + goto FAIL_CLEANUP; + } + } + + /* connect to server, might fail of course */ + { + struct sockaddr_un addr; + socklen_t addr_len; + + /* intialize address structure */ + addr.sun_family = AF_UNIX; + g_snprintf(addr.sun_path, + sizeof(addr.sun_path), + "%s/.dropbox/iface_socket", + g_get_home_dir()); + addr_len = sizeof(addr) - sizeof(addr.sun_path) + strlen(addr.sun_path); + + /* if there was an error we have to try again later */ + if (connect(hookserv->socket, (struct sockaddr *) &addr, addr_len) < 0) { + if (errno == EINPROGRESS) { + fd_set writers; + struct timeval tv = {1, 0}; + FD_ZERO(&writers); + FD_SET(hookserv->socket, &writers); + + /* if nothing was ready after 3 seconds, fail out homie */ + if (select(hookserv->socket+1, NULL, &writers, NULL, &tv) == 0) { + goto FAIL_CLEANUP; + } + + if (connect(hookserv->socket, (struct sockaddr *) &addr, addr_len) < 0) { + debug("couldn't connect to hook server after 1 second"); + goto FAIL_CLEANUP; + } + } + else { + goto FAIL_CLEANUP; + } + } + } + + /* lol sometimes i write funny codez */ + if (FALSE) { + FAIL_CLEANUP: + close(hookserv->socket); + g_timeout_add_seconds(1, (GSourceFunc) try_to_connect, hookserv); + return FALSE; + } + + /* great we connected!, let's create the channel and wait on it */ + hookserv->chan = g_io_channel_unix_new(hookserv->socket); + g_io_channel_set_line_term(hookserv->chan, "\n", -1); + g_io_channel_set_close_on_unref(hookserv->chan, TRUE); + + /*debug("create channel"); */ + + /* Set non-blocking ;) (again just in case) */ + { + GIOFlags flags; + GIOStatus iostat; + + flags = g_io_channel_get_flags(hookserv->chan); + iostat = g_io_channel_set_flags(hookserv->chan, flags | G_IO_FLAG_NONBLOCK, + NULL); + if (iostat == G_IO_STATUS_ERROR) { + g_io_channel_unref(hookserv->chan); + g_timeout_add_seconds(1, (GSourceFunc) try_to_connect, hookserv); + return FALSE; + } + } + + /*debug("set non blocking"); */ + + /* this is fun, async io watcher */ + hookserv->hhsi.line = 0; + hookserv->hhsi.command_args = NULL; + hookserv->hhsi.command_name = NULL; + hookserv->event_source = + g_io_add_watch_full(hookserv->chan, G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) handle_hook_server_input, hookserv, + (GDestroyNotify) watch_killer); + + debug("hook client connected"); + hookserv->connected = TRUE; + g_hook_list_invoke(&(hookserv->onconnect_hooklist), FALSE); + + /*debug("added watch");*/ + return FALSE; +} + +/* should only be called in glib main loop */ +/* returns a gboolean because it is a GSourceFunc */ +gboolean nautilus_dropbox_hooks_force_reconnect(NautilusDropboxHookserv *hookserv) { + debug_enter(); + + if (hookserv->connected == FALSE) { + return FALSE; + } + + debug("forcing hook to reconnect"); + + g_assert(hookserv->event_source >= 0); + + if (hookserv->event_source > 0) { + g_source_remove(hookserv->event_source); + } + else if (hookserv->event_source == 0) { + debug("event source was zero!!!!!"); + } + + return FALSE; +} + +gboolean +nautilus_dropbox_hooks_is_connected(NautilusDropboxHookserv *hookserv) { + return hookserv->connected; +} + +void +nautilus_dropbox_hooks_setup(NautilusDropboxHookserv *hookserv) { + hookserv->dispatch_table = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + g_free, g_free); + hookserv->connected = FALSE; + + g_hook_list_init(&(hookserv->ondisconnect_hooklist), sizeof(GHook)); + g_hook_list_init(&(hookserv->onconnect_hooklist), sizeof(GHook)); +} + +void +nautilus_dropbox_hooks_add_on_disconnect_hook(NautilusDropboxHookserv *hookserv, + DropboxHookClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(hookserv->ondisconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(hookserv->ondisconnect_hooklist), newhook); +} + +void +nautilus_dropbox_hooks_add_on_connect_hook(NautilusDropboxHookserv *hookserv, + DropboxHookClientConnectHook dhcch, + gpointer ud) { + GHook *newhook; + + newhook = g_hook_alloc(&(hookserv->onconnect_hooklist)); + newhook->func = dhcch; + newhook->data = ud; + + g_hook_append(&(hookserv->onconnect_hooklist), newhook); +} + +void nautilus_dropbox_hooks_add(NautilusDropboxHookserv *ndhs, + const gchar *hook_name, + DropboxUpdateHook hook, gpointer ud) { + HookData *hd; + hd = g_new(HookData, 1); + hd->hook = hook; + hd->ud = ud; + g_hash_table_insert(ndhs->dispatch_table, g_strdup(hook_name), hd); +} + +void +nautilus_dropbox_hooks_start(NautilusDropboxHookserv *hookserv) { + try_to_connect(hookserv); +} diff --git a/src/nautilus-dropbox-hooks.h b/src/nautilus-dropbox-hooks.h new file mode 100644 index 0000000..fb69c3b --- /dev/null +++ b/src/nautilus-dropbox-hooks.h @@ -0,0 +1,79 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * nautilus-dropbox-hooks.h + * Header file for nautilus-dropbox-hooks.c + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef NAUTILUS_DROPBOX_HOOKS_H +#define NAUTILUS_DROPBOX_HOOKS_H + +#include <glib.h> + +G_BEGIN_DECLS + +typedef void (*DropboxUpdateHook)(GHashTable *, gpointer); +typedef void (*DropboxHookClientConnectHook)(gpointer); + +typedef struct { + GIOChannel *chan; + int socket; + struct { + int line; + gchar *command_name; + GHashTable *command_args; + int numargs; + } hhsi; + gboolean connected; + guint event_source; + GHashTable *dispatch_table; + GHookList ondisconnect_hooklist; + GHookList onconnect_hooklist; +} NautilusDropboxHookserv; + +void +nautilus_dropbox_hooks_setup(NautilusDropboxHookserv *); + +void +nautilus_dropbox_hooks_start(NautilusDropboxHookserv *); + +gboolean +nautilus_dropbox_hooks_is_connected(NautilusDropboxHookserv *); + +gboolean +nautilus_dropbox_hooks_force_reconnect(NautilusDropboxHookserv *); + +void +nautilus_dropbox_hooks_add(NautilusDropboxHookserv *ndhs, + const gchar *hook_name, + DropboxUpdateHook hook, gpointer ud); +void +nautilus_dropbox_hooks_add_on_disconnect_hook(NautilusDropboxHookserv *hookserv, + DropboxHookClientConnectHook dhcch, + gpointer ud); + +void +nautilus_dropbox_hooks_add_on_connect_hook(NautilusDropboxHookserv *hookserv, + DropboxHookClientConnectHook dhcch, + gpointer ud); + + +G_END_DECLS + +#endif diff --git a/src/nautilus-dropbox.c b/src/nautilus-dropbox.c new file mode 100644 index 0000000..86a0e8a --- /dev/null +++ b/src/nautilus-dropbox.c @@ -0,0 +1,960 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * nautilus-dropbox.c + * Implements the Nautilus extension API for Dropbox. + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> /* for GETTEXT_PACKAGE */ +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <glib-object.h> +#include <gtk/gtk.h> + +#include <libnautilus-extension/nautilus-extension-types.h> +#include <libnautilus-extension/nautilus-menu-provider.h> +#include <libnautilus-extension/nautilus-info-provider.h> + +#include "g-util.h" +#include "dropbox-command-client.h" +#include "nautilus-dropbox.h" +#include "nautilus-dropbox-hooks.h" + +static char *emblems[] = {"dropbox-uptodate", "dropbox-syncing", "dropbox-unsyncable"}; +gchar *DEFAULT_EMBLEM_PATHS[2] = { EMBLEMDIR , NULL }; + +gboolean dropbox_use_nautilus_submenu_workaround; +gboolean dropbox_use_operation_in_progress_workaround; + +static GType dropbox_type = 0; + +/* for old versions of glib */ +#if 0 // Silence Warnings. +static void my_g_hash_table_get_keys_helper(gpointer key, + gpointer value, + GList **ud) { + *ud = g_list_append(*ud, key); +} + +static GList *my_g_hash_table_get_keys(GHashTable *ght) { + GList *list = NULL; + g_hash_table_foreach(ght, (GHFunc) my_g_hash_table_get_keys_helper, &list); + return list; +} +#endif + +/* probably my favorite function */ +static gchar * +canonicalize_path(gchar *path) { + int i, j = 0; + gchar *toret, **cpy, **elts; + + g_assert(path != NULL); + g_assert(path[0] == '/'); + + elts = g_strsplit(path, "/", 0); + cpy = g_new(gchar *, g_strv_length(elts)+1); + cpy[j++] = "/"; + for (i = 0; elts[i] != NULL; i++) { + if (strcmp(elts[i], "..") == 0) { + j--; + } + else if (strcmp(elts[i], ".") != 0 && elts[i][0] != '\0') { + cpy[j++] = elts[i]; + } + } + + cpy[j] = NULL; + toret = g_build_filenamev(cpy); + g_free(cpy); + g_strfreev(elts); + + return toret; +} + +static void +reset_file(NautilusFileInfo *file) { + debug("resetting file %p", (void *) file); + nautilus_file_info_invalidate_extension_info(file); +} + +gboolean +reset_all_files(NautilusDropbox *cvs) { + /* Only run this on the main loop or you'll cause problems. */ + + /* this works because you can call a function pointer with + more arguments than it takes */ + g_hash_table_foreach(cvs->obj2filename, (GHFunc) reset_file, NULL); + return FALSE; +} + + +static void +when_file_dies(NautilusDropbox *cvs, NautilusFileInfo *address) { + gchar *filename; + + filename = g_hash_table_lookup(cvs->obj2filename, address); + + /* we never got a change to view this file */ + if (filename == NULL) { + return; + } + + /* too chatty */ + /* debug("removing %s <-> 0x%p", filename, address); */ + + g_hash_table_remove(cvs->filename2obj, filename); + g_hash_table_remove(cvs->obj2filename, address); +} + +static void +changed_cb(NautilusFileInfo *file, NautilusDropbox *cvs) { + /* check if this file's path has changed, if so update the hash and invalidate + the file */ + gchar *filename, *pfilename; + gchar *filename2; + gchar *uri; + + uri = nautilus_file_info_get_uri(file); + pfilename = g_filename_from_uri(uri, NULL, NULL); + filename = pfilename ? canonicalize_path(pfilename) : NULL; + filename2 = g_hash_table_lookup(cvs->obj2filename, file); + + g_free(pfilename); + g_free(uri); + + /* if filename2 is NULL we've never seen this file in update_file_info */ + if (filename2 == NULL) { + g_free(filename); + return; + } + + if (filename == NULL) { + /* A file has moved to offline storage. Lets remove it from our tables. */ + g_object_weak_unref(G_OBJECT(file), (GWeakNotify) when_file_dies, cvs); + g_hash_table_remove(cvs->filename2obj, filename2); + g_hash_table_remove(cvs->obj2filename, file); + g_signal_handlers_disconnect_by_func(file, G_CALLBACK(changed_cb), cvs); + reset_file(file); + return; + } + + /* this is a hack, because nautilus doesn't do this for us, for some reason + the file's path has changed */ + if (strcmp(filename, filename2) != 0) { + debug("shifty old: %s, new %s", filename2, filename); + + /* gotta do this first, the call after this frees filename2 */ + g_hash_table_remove(cvs->filename2obj, filename2); + + g_hash_table_replace(cvs->obj2filename, file, g_strdup(filename)); + + { + NautilusFileInfo *f2; + /* we shouldn't have another mapping from filename to an object */ + f2 = g_hash_table_lookup(cvs->filename2obj, filename); + if (f2 != NULL) { + /* lets fix it if it's true, just remove the mapping */ + g_hash_table_remove(cvs->filename2obj, filename); + g_hash_table_remove(cvs->obj2filename, f2); + } + } + + g_hash_table_insert(cvs->filename2obj, g_strdup(filename), file); + reset_file(file); + } + + g_free(filename); +} + +static NautilusOperationResult +nautilus_dropbox_update_file_info(NautilusInfoProvider *provider, + NautilusFileInfo *file, + GClosure *update_complete, + NautilusOperationHandle **handle) { + NautilusDropbox *cvs; + + cvs = NAUTILUS_DROPBOX(provider); + + /* this code adds this file object to our two-way hash of file objects + so we can shell touch these files later */ + { + gchar *pfilename, *uri; + + uri = nautilus_file_info_get_uri(file); + pfilename = g_filename_from_uri(uri, NULL, NULL); + g_free(uri); + if (pfilename == NULL) { + return NAUTILUS_OPERATION_COMPLETE; + } + else { + int cmp = 0; + gchar *stored_filename; + gchar *filename; + + filename = canonicalize_path(pfilename); + g_free(pfilename); + stored_filename = g_hash_table_lookup(cvs->obj2filename, file); + + /* don't worry about the dup checks, gcc is smart enough to optimize this + GCSE ftw */ + if ((stored_filename != NULL && (cmp = strcmp(stored_filename, filename)) != 0) || + stored_filename == NULL) { + + if (stored_filename != NULL && cmp != 0) { + /* this happens when the filename changes name on a file obj + but changed_cb isn't called */ + g_object_weak_unref(G_OBJECT(file), (GWeakNotify) when_file_dies, cvs); + g_hash_table_remove(cvs->obj2filename, file); + g_hash_table_remove(cvs->filename2obj, stored_filename); + g_signal_handlers_disconnect_by_func(file, G_CALLBACK(changed_cb), cvs); + } + else if (stored_filename == NULL) { + NautilusFileInfo *f2; + + if ((f2 = g_hash_table_lookup(cvs->filename2obj, filename)) != NULL) { + /* if the filename exists in the filename2obj hash + but the file obj doesn't exist in the obj2filename hash: + + this happens when nautilus allocates another file object + for a filename without first deleting the original file object + + just remove the association to the older file object, it's obsolete + */ + g_object_weak_unref(G_OBJECT(f2), (GWeakNotify) when_file_dies, cvs); + g_signal_handlers_disconnect_by_func(f2, G_CALLBACK(changed_cb), cvs); + g_hash_table_remove(cvs->filename2obj, filename); + g_hash_table_remove(cvs->obj2filename, f2); + } + } + + /* too chatty */ + /* debug("adding %s <-> 0x%p", filename, file);*/ + g_object_weak_ref(G_OBJECT(file), (GWeakNotify) when_file_dies, cvs); + g_hash_table_insert(cvs->filename2obj, g_strdup(filename), file); + g_hash_table_insert(cvs->obj2filename, file, g_strdup(filename)); + g_signal_connect(file, "changed", G_CALLBACK(changed_cb), cvs); + } + + g_free(filename); + } + } + + if (dropbox_client_is_connected(&(cvs->dc)) == FALSE || + nautilus_file_info_is_gone(file)) { + return NAUTILUS_OPERATION_COMPLETE; + } + + { + DropboxFileInfoCommand *dfic = g_new0(DropboxFileInfoCommand, 1); + + dfic->cancelled = FALSE; + dfic->provider = provider; + dfic->dc.request_type = GET_FILE_INFO; + dfic->update_complete = g_closure_ref(update_complete); + dfic->file = g_object_ref(file); + + dropbox_command_client_request(&(cvs->dc.dcc), (DropboxCommand *) dfic); + + *handle = (NautilusOperationHandle *) dfic; + + return dropbox_use_operation_in_progress_workaround + ? NAUTILUS_OPERATION_COMPLETE + : NAUTILUS_OPERATION_IN_PROGRESS; + } +} + +static void +handle_shell_touch(GHashTable *args, NautilusDropbox *cvs) { + gchar **path; + + // debug_enter(); + + if ((path = g_hash_table_lookup(args, "path")) != NULL && + path[0][0] == '/') { + NautilusFileInfo *file; + gchar *filename; + + filename = canonicalize_path(path[0]); + + debug("shell touch for %s", filename); + + file = g_hash_table_lookup(cvs->filename2obj, filename); + + if (file != NULL) { + debug("gonna reset %s", filename); + reset_file(file); + } + g_free(filename); + } + + return; +} + +gboolean +nautilus_dropbox_finish_file_info_command(DropboxFileInfoCommandResponse *dficr) { + + //debug_enter(); + NautilusOperationResult result = NAUTILUS_OPERATION_FAILED; + + if (!dficr->dfic->cancelled) { + gchar **status = NULL; + gboolean isdir; + + isdir = nautilus_file_info_is_directory(dficr->dfic->file) ; + + /* if we have emblems just use them. */ + if (dficr->emblems_response != NULL && + (status = g_hash_table_lookup(dficr->emblems_response, "emblems")) != NULL) { + int i; + for ( i = 0; status[i] != NULL; i++) { + if (status[i][0]) + nautilus_file_info_add_emblem(dficr->dfic->file, status[i]); + } + result = NAUTILUS_OPERATION_COMPLETE; + } + /* if the file status command went okay */ + else if ((dficr->file_status_response != NULL && + (status = + g_hash_table_lookup(dficr->file_status_response, "status")) != NULL) && + ((isdir == TRUE && + dficr->folder_tag_response != NULL) || isdir == FALSE)) { + gchar **tag = NULL; + + /* set the tag emblem */ + if (isdir && + (tag = g_hash_table_lookup(dficr->folder_tag_response, "tag")) != NULL) { + if (strcmp("public", tag[0]) == 0) { + nautilus_file_info_add_emblem(dficr->dfic->file, "web"); + } + else if (strcmp("shared", tag[0]) == 0) { + nautilus_file_info_add_emblem(dficr->dfic->file, "people"); + } + else if (strcmp("photos", tag[0]) == 0) { + nautilus_file_info_add_emblem(dficr->dfic->file, "photos"); + } + else if (strcmp("sandbox", tag[0]) == 0) { + nautilus_file_info_add_emblem(dficr->dfic->file, "star"); + } + } + + /* set the status emblem */ + { + int emblem_code = 0; + + if (strcmp("up to date", status[0]) == 0) { + emblem_code = 1; + } + else if (strcmp("syncing", status[0]) == 0) { + emblem_code = 2; + } + else if (strcmp("unsyncable", status[0]) == 0) { + emblem_code = 3; + } + + if (emblem_code > 0) { + /* + debug("%s to %s", emblems[emblem_code-1], + g_filename_from_uri(nautilus_file_info_get_uri(dficr->dfic->file), + NULL, NULL)); + */ + nautilus_file_info_add_emblem(dficr->dfic->file, emblems[emblem_code-1]); + } + } + result = NAUTILUS_OPERATION_COMPLETE; + } + } + + /* complete the info request */ + if (!dropbox_use_operation_in_progress_workaround) { + nautilus_info_provider_update_complete_invoke(dficr->dfic->update_complete, + dficr->dfic->provider, + (NautilusOperationHandle*) dficr->dfic, + result); + } + + /* destroy the objects we created */ + if (dficr->file_status_response != NULL) + g_hash_table_unref(dficr->file_status_response); + if (dficr->folder_tag_response != NULL) + g_hash_table_unref(dficr->folder_tag_response); + if (dficr->emblems_response != NULL) + g_hash_table_unref(dficr->emblems_response); + + /* unref the objects we didn't create */ + g_closure_unref(dficr->dfic->update_complete); + g_object_unref(dficr->dfic->file); + + /* now free the structs */ + g_free(dficr->dfic); + g_free(dficr); + + return FALSE; +} + +static void +nautilus_dropbox_cancel_update(NautilusInfoProvider *provider, + NautilusOperationHandle *handle) { + DropboxFileInfoCommand *dfic = (DropboxFileInfoCommand *) handle; + dfic->cancelled = TRUE; + return; +} + +static void +menu_item_cb(NautilusMenuItem *item, + NautilusDropbox *cvs) { + gchar *verb; + GList *files; + DropboxGeneralCommand *dcac; + + dcac = g_new(DropboxGeneralCommand, 1); + + /* maybe these would be better passed in a container + struct used as the userdata pointer, oh well this + is how dave camp does it */ + files = g_object_get_data(G_OBJECT(item), "nautilus_dropbox_files"); + verb = g_object_get_data(G_OBJECT(item), "nautilus_dropbox_verb"); + + dcac->dc.request_type = GENERAL_COMMAND; + + /* build the argument list */ + dcac->command_args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + { + gchar **arglist; + guint i; + GList *li; + + arglist = g_new0(gchar *,g_list_length(files) + 1); + + for (li = files, i = 0; li != NULL; li = g_list_next(li)) { + char *uri = nautilus_file_info_get_uri(NAUTILUS_FILE_INFO(li->data)); + char *path = g_filename_from_uri(uri, NULL, NULL); + g_free(uri); + if (!path) + continue; + arglist[i] = path; + i++; + } + + g_hash_table_insert(dcac->command_args, + g_strdup("paths"), + arglist); + } + + { + gchar **arglist; + arglist = g_new(gchar *, 2); + arglist[0] = g_strdup(verb); + arglist[1] = NULL; + g_hash_table_insert(dcac->command_args, g_strdup("verb"), arglist); + } + + dcac->command_name = g_strdup("icon_overlay_context_action"); + dcac->handler = NULL; + dcac->handler_ud = NULL; + + dropbox_command_client_request(&(cvs->dc.dcc), (DropboxCommand *) dcac); +} + +static char from_hex(gchar ch) { + return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; +} + +// decode in --> out, but dont fill more than n chars into out +// returns len of out if thing went well, -1 if n wasn't big enough +// can be used in place (whoa!) +int GhettoURLDecode(gchar* out, gchar* in, int n) { + char *out_initial; + + for(out_initial = out; out-out_initial < n && *in != '\0'; out++) { + if (*in == '%') { + *out = from_hex(in[1]) << 4 | from_hex(in[2]); + in += 3; + } + else { + *out = *in; + in++; + } + } + + if (out-out_initial < n) { + *out = '\0'; + return out-out_initial; + } + return -1; +} + +static int +nautilus_dropbox_parse_menu(gchar **options, + NautilusMenu *menu, + GString *old_action_string, + GList *toret, + NautilusMenuProvider *provider, + GList *files) +{ + int ret = 0; + int i; + + for ( i = 0; options[i] != NULL; i++) { + gchar **option_info = g_strsplit(options[i], "~", 3); + /* if this is a valid string */ + if (option_info[0] == NULL || option_info[1] == NULL || + option_info[2] == NULL || option_info[3] != NULL) { + g_strfreev(option_info); + continue; + } + + gchar* item_name = option_info[0]; + gchar* item_inner = option_info[1]; + gchar* verb = option_info[2]; + + GhettoURLDecode(item_name, item_name, strlen(item_name)); + GhettoURLDecode(verb, verb, strlen(verb)); + GhettoURLDecode(item_inner, item_inner, strlen(item_inner)); + + // If the inner section has a menu in it then we create a submenu. The verb will be ignored. + // Otherwise add the verb to our map and add the menu item to the list. + if (strchr(item_inner, '~') != NULL) { + GString *new_action_string = g_string_new(old_action_string->str); + gchar **suboptions = g_strsplit(item_inner, "|", -1); + NautilusMenuItem *item; + NautilusMenu *submenu = nautilus_menu_new(); + + g_string_append(new_action_string, item_name); + g_string_append(new_action_string, "::"); + + ret += nautilus_dropbox_parse_menu(suboptions, submenu, new_action_string, + toret, provider, files); + + item = nautilus_menu_item_new(new_action_string->str, + item_name, "", NULL); + nautilus_menu_item_set_submenu(item, submenu); + nautilus_menu_append_item(menu, item); + + g_strfreev(suboptions); + g_object_unref(item); + g_object_unref(submenu); + g_string_free(new_action_string, TRUE); + } else { + NautilusMenuItem *item; + GString *new_action_string = g_string_new(old_action_string->str); + gboolean grayed_out = FALSE; + + g_string_append(new_action_string, verb); + + if (item_name[0] == '!') { + item_name++; + grayed_out = TRUE; + } + + item = nautilus_menu_item_new(new_action_string->str, item_name, item_inner, NULL); + + nautilus_menu_append_item(menu, item); + /* add the file metadata to this item */ + g_object_set_data_full (G_OBJECT(item), "nautilus_dropbox_files", + nautilus_file_info_list_copy (files), + (GDestroyNotify) nautilus_file_info_list_free); + /* add the verb metadata */ + g_object_set_data_full (G_OBJECT(item), "nautilus_dropbox_verb", + g_strdup(verb), + (GDestroyNotify) g_free); + g_signal_connect (item, "activate", G_CALLBACK (menu_item_cb), provider); + + if (grayed_out) { + GValue sensitive = { 0 }; + g_value_init (&sensitive, G_TYPE_BOOLEAN); + g_value_set_boolean (&sensitive, FALSE); + g_object_set_property (G_OBJECT(item), "sensitive", &sensitive); + } + + /* taken from nautilus-file-repairer (http://repairer.kldp.net/): + * this code is a workaround for a bug of nautilus + * See: http://bugzilla.gnome.org/show_bug.cgi?id=508878 */ + if (dropbox_use_nautilus_submenu_workaround) { + toret = g_list_append(toret, item); + } + + g_object_unref(item); + g_string_free(new_action_string, TRUE); + ret++; + } + g_strfreev(option_info); + } + return ret; +} + +static void +get_file_items_callback(GHashTable *response, gpointer ud) +{ + GAsyncQueue *reply_queue = ud; + + /* queue_push doesn't accept NULL as a value so we create an empty hash table + * if we got no response. */ + g_async_queue_push(reply_queue, response ? g_hash_table_ref(response) : + g_hash_table_new((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal)); + g_async_queue_unref(reply_queue); +} + + +static GList * +nautilus_dropbox_get_file_items(NautilusMenuProvider *provider, + GtkWidget *window, + GList *files) +{ + /* + * 1. Convert files to filenames. + */ + int file_count = g_list_length(files); + + if (file_count < 1) + return NULL; + + gchar **paths = g_new0(gchar *, file_count + 1); + int i = 0; + GList* elem; + + for (elem = files; elem; elem = elem->next, i++) { + gchar *uri = nautilus_file_info_get_uri(elem->data); + gchar *filename_un = uri ? g_filename_from_uri(uri, NULL, NULL) : NULL; + gchar *filename = filename_un ? g_filename_to_utf8(filename_un, -1, NULL, NULL, NULL) : NULL; + + g_free(uri); + g_free(filename_un); + + if (filename == NULL) { + /* oooh, filename wasn't correctly encoded, or isn't a local file. */ + g_strfreev(paths); + return NULL; + } + + paths[i] = filename; + } + + GAsyncQueue *reply_queue = g_async_queue_new_full((GDestroyNotify)g_hash_table_unref); + + /* + * 2. Create a DropboxGeneralCommand to call "icon_overlay_context_options" + */ + + DropboxGeneralCommand *dgc = g_new0(DropboxGeneralCommand, 1); + dgc->dc.request_type = GENERAL_COMMAND; + dgc->command_name = g_strdup("icon_overlay_context_options"); + dgc->command_args = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_strfreev); + g_hash_table_insert(dgc->command_args, g_strdup("paths"), paths); + dgc->handler = get_file_items_callback; + dgc->handler_ud = g_async_queue_ref(reply_queue); + + /* + * 3. Queue it up for the helper thread to run it. + */ + NautilusDropbox *cvs = NAUTILUS_DROPBOX(provider); + dropbox_command_client_request(&(cvs->dc.dcc), (DropboxCommand *) dgc); + + GTimeVal gtv; + + /* + * 4. We have to block until it's done because nautilus expects a reply. But we will + * only block for 50 ms for a reply. + */ + + g_get_current_time(>v); + g_time_val_add(>v, 50000); + + GHashTable *context_options_response = g_async_queue_timed_pop(reply_queue, >v); + g_async_queue_unref(reply_queue); + + if (!context_options_response) { + return NULL; + } + + /* + * 5. Parse the reply. + */ + + char **options = g_hash_table_lookup(context_options_response, "options"); + GList *toret = NULL; + + if (options && *options && **options) { + /* build the menu */ + NautilusMenuItem *root_item; + NautilusMenu *root_menu; + + root_menu = nautilus_menu_new(); + root_item = nautilus_menu_item_new("NautilusDropbox::root_item", + "Dropbox", "Dropbox Options", "dropbox"); + + toret = g_list_append(toret, root_item); + GString *action_string = g_string_new("NautilusDropbox::"); + + if (!nautilus_dropbox_parse_menu(options, root_menu, action_string, + toret, provider, files)) { + g_object_unref(toret); + toret = NULL; + } + + nautilus_menu_item_set_submenu(root_item, root_menu); + + g_string_free(action_string, TRUE); + g_object_unref(root_menu); + } + + g_hash_table_unref(context_options_response); + + return toret; +} + +gboolean +add_emblem_paths(GHashTable* emblem_paths_response) +{ + /* Only run this on the main loop or you'll cause problems. */ + if (!emblem_paths_response) + return FALSE; + + gchar **emblem_paths_list; + int i; + + GtkIconTheme *theme = gtk_icon_theme_get_default(); + + if (emblem_paths_response && + (emblem_paths_list = g_hash_table_lookup(emblem_paths_response, "path"))) { + for (i = 0; emblem_paths_list[i] != NULL; i++) { + if (emblem_paths_list[i][0]) + gtk_icon_theme_append_search_path(theme, emblem_paths_list[i]); + } + } + g_hash_table_unref(emblem_paths_response); + return FALSE; +} + +gboolean +remove_emblem_paths(GHashTable* emblem_paths_response) +{ + /* Only run this on the main loop or you'll cause problems. */ + if (!emblem_paths_response) + return FALSE; + + gchar **emblem_paths_list = g_hash_table_lookup(emblem_paths_response, "path"); + if (!emblem_paths_list) + goto exit; + + // We need to remove the old paths. + GtkIconTheme * icon_theme = gtk_icon_theme_get_default(); + gchar ** paths; + gint path_count; + + gtk_icon_theme_get_search_path(icon_theme, &paths, &path_count); + + gint i, j, out = 0; + gboolean found = FALSE; + for (i = 0; i < path_count; i++) { + gboolean keep = TRUE; + for (j = 0; emblem_paths_list[j] != NULL; j++) { + if (emblem_paths_list[j][0]) { + if (!g_strcmp0(paths[i], emblem_paths_list[j])) { + found = TRUE; + keep = FALSE; + g_free(paths[i]); + break; + } + } + } + if (keep) { + paths[out] = paths[i]; + out++; + } + } + + /* If we found one we need to reset the path to + accomodate the changes */ + if (found) { + paths[out] = NULL; /* Clear the last one */ + gtk_icon_theme_set_search_path(icon_theme, (const gchar **)paths, out); + } + + g_strfreev(paths); +exit: + g_hash_table_unref(emblem_paths_response); + return FALSE; +} + +void get_emblem_paths_cb(GHashTable *emblem_paths_response, NautilusDropbox *cvs) +{ + if (!emblem_paths_response) { + emblem_paths_response = g_hash_table_new((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal); + g_hash_table_insert(emblem_paths_response, "path", DEFAULT_EMBLEM_PATHS); + } else { + /* Increase the ref so that finish_general_command doesn't delete it. */ + g_hash_table_ref(emblem_paths_response); + } + + g_mutex_lock(cvs->emblem_paths_mutex); + if (cvs->emblem_paths) { + g_idle_add((GSourceFunc) remove_emblem_paths, cvs->emblem_paths); + cvs->emblem_paths = NULL; + } + cvs->emblem_paths = emblem_paths_response; + g_mutex_unlock(cvs->emblem_paths_mutex); + + g_idle_add((GSourceFunc) add_emblem_paths, g_hash_table_ref(emblem_paths_response)); + g_idle_add((GSourceFunc) reset_all_files, cvs); +} + +static void +on_connect(NautilusDropbox *cvs) { + reset_all_files(cvs); + + dropbox_command_client_send_command(&(cvs->dc.dcc), + (NautilusDropboxCommandResponseHandler) get_emblem_paths_cb, + cvs, "get_emblem_paths", NULL); +} + +static void +on_disconnect(NautilusDropbox *cvs) { + reset_all_files(cvs); + + g_mutex_lock(cvs->emblem_paths_mutex); + /* This call will free the data too. */ + g_idle_add((GSourceFunc) remove_emblem_paths, cvs->emblem_paths); + cvs->emblem_paths = NULL; + g_mutex_unlock(cvs->emblem_paths_mutex); +} + + +static void +nautilus_dropbox_menu_provider_iface_init (NautilusMenuProviderIface *iface) { + iface->get_file_items = nautilus_dropbox_get_file_items; + return; +} + +static void +nautilus_dropbox_info_provider_iface_init (NautilusInfoProviderIface *iface) { + iface->update_file_info = nautilus_dropbox_update_file_info; + iface->cancel_update = nautilus_dropbox_cancel_update; + return; +} + +static void +nautilus_dropbox_instance_init (NautilusDropbox *cvs) { + cvs->filename2obj = g_hash_table_new_full((GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + cvs->obj2filename = g_hash_table_new_full((GHashFunc) g_direct_hash, + (GEqualFunc) g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_free); + cvs->emblem_paths_mutex = g_mutex_new(); + cvs->emblem_paths = NULL; + + /* setup the connection obj*/ + dropbox_client_setup(&(cvs->dc)); + + /* our hooks */ + nautilus_dropbox_hooks_add(&(cvs->dc.hookserv), "shell_touch", + (DropboxUpdateHook) handle_shell_touch, cvs); + + /* add connection handlers */ + dropbox_client_add_on_connect_hook(&(cvs->dc), + (DropboxClientConnectHook) on_connect, + cvs); + dropbox_client_add_on_disconnect_hook(&(cvs->dc), + (DropboxClientConnectHook) on_disconnect, + cvs); + + /* now start the connection */ + debug("about to start client connection"); + dropbox_client_start(&(cvs->dc)); + + return; +} + +static void +nautilus_dropbox_class_init (NautilusDropboxClass *class) { +} + +static void +nautilus_dropbox_class_finalize (NautilusDropboxClass *class) { + debug("just checking"); + /* kill threads here? */ +} + +GType +nautilus_dropbox_get_type (void) { + return dropbox_type; +} + +void +nautilus_dropbox_register_type (GTypeModule *module) { + static const GTypeInfo info = { + sizeof (NautilusDropboxClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) nautilus_dropbox_class_init, + (GClassFinalizeFunc) nautilus_dropbox_class_finalize, + NULL, + sizeof (NautilusDropbox), + 0, + (GInstanceInitFunc) nautilus_dropbox_instance_init, + }; + + static const GInterfaceInfo menu_provider_iface_info = { + (GInterfaceInitFunc) nautilus_dropbox_menu_provider_iface_init, + NULL, + NULL + }; + + static const GInterfaceInfo info_provider_iface_info = { + (GInterfaceInitFunc) nautilus_dropbox_info_provider_iface_init, + NULL, + NULL + }; + + dropbox_type = + g_type_module_register_type(module, + G_TYPE_OBJECT, + "NautilusDropbox", + &info, 0); + + g_type_module_add_interface (module, + dropbox_type, + NAUTILUS_TYPE_MENU_PROVIDER, + &menu_provider_iface_info); + + g_type_module_add_interface (module, + dropbox_type, + NAUTILUS_TYPE_INFO_PROVIDER, + &info_provider_iface_info); +} diff --git a/src/nautilus-dropbox.h b/src/nautilus-dropbox.h new file mode 100644 index 0000000..65734be --- /dev/null +++ b/src/nautilus-dropbox.h @@ -0,0 +1,69 @@ +/* + * Copyright 2008 Evenflow, Inc. + * + * nautilus-dropbox.h + * Header file for nautilus-dropbox.c + * + * This file is part of nautilus-dropbox. + * + * nautilus-dropbox 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. + * + * nautilus-dropbox 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 nautilus-dropbox. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef NAUTILUS_DROPBOX_H +#define NAUTILUS_DROPBOX_H + +#include <glib.h> +#include <glib-object.h> + +#include <libnautilus-extension/nautilus-info-provider.h> + +#include "dropbox-command-client.h" +#include "nautilus-dropbox-hooks.h" +#include "dropbox-client.h" + +G_BEGIN_DECLS + +/* Declarations for the dropbox extension object. This object will be + * instantiated by nautilus. It implements the GInterfaces + * exported by libnautilus. */ + +#define NAUTILUS_TYPE_DROPBOX (nautilus_dropbox_get_type ()) +#define NAUTILUS_DROPBOX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NAUTILUS_TYPE_DROPBOX, NautilusDropbox)) +#define NAUTILUS_IS_DROPBOX(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NAUTILUS_TYPE_DROPBOX)) +typedef struct _NautilusDropbox NautilusDropbox; +typedef struct _NautilusDropboxClass NautilusDropboxClass; + +struct _NautilusDropbox { + GObject parent_slot; + GHashTable *filename2obj; + GHashTable *obj2filename; + GMutex *emblem_paths_mutex; + GHashTable *emblem_paths; + DropboxClient dc; +}; + +struct _NautilusDropboxClass { + GObjectClass parent_slot; +}; + +GType nautilus_dropbox_get_type(void); +void nautilus_dropbox_register_type(GTypeModule *module); + +extern gboolean dropbox_use_nautilus_submenu_workaround; +extern gboolean dropbox_use_operation_in_progress_workaround; + +G_END_DECLS + +#endif |