Compare commits

...

494 Commits

Author SHA1 Message Date
Vitaliy Filippov d03c4a24ce Bump version to 0.5.3 2022-11-09 12:45:14 +03:00
Vitaliy Filippov 328987ec34 Fix readme 2022-11-09 12:41:47 +03:00
Vitaliy Filippov 6645206d27 Implement Google OAuth loopback IP address flow (fixes #362 #372 #365) 2022-11-09 12:37:18 +03:00
Vitaliy Filippov 6901fbb169
Merge pull request #345 from ncaq/fix-shell-script-path
fixed: CMAKE_INSTALL_FULL_LIBDIR -> CMAKE_INSTALL_FULL_LIBEXECDIR
2021-11-29 00:13:19 +03:00
ncaq 48f5f0e52f fixed: CMAKE_INSTALL_FULL_LIBDIR -> CMAKE_INSTALL_FULL_LIBEXECDIR
Because, It must equal GRIVE_SYNC_SH_BINARY and actual grive-sync.sh path.
I patched my gentoo overlay ebuild.
[added: grive: libexec patch · ncaq/ncaq-overlay@ed3354f](ed3354fbb7)
2021-07-08 14:41:19 +09:00
Vitaliy Filippov e6fcc637f8
Merge pull request #245 from jankatins/revert_symlink_handling_in_sync
Revert "Exclude symlinks from sync"
2021-05-27 23:44:58 +03:00
Vitaliy Filippov dedb762b74
Merge pull request #246 from jankatins/add-main-systemd-unit
Add systemd unit to enable both timer and changes based syncs
2021-05-27 22:50:41 +03:00
Vitaliy Filippov d55fe7f5eb
Merge pull request #344 from agusalex/patch-1
Fix Dockerfile
2021-05-14 22:41:38 +03:00
Agustin Alexander 03a2c58403
Fix Dockerfile 2021-05-14 13:47:14 -03:00
Vitaliy Filippov b112fccd37
Merge pull request #325 from agusalex/master
Optimized (using Alpine) Docker image size (compressed) 214MB -> 62MB
2021-05-14 18:57:10 +03:00
Vitaliy Filippov cb3c586862
Merge pull request #310 from mbartlet/systemd-escape
fix systemd-escape hyphen usage
2021-05-14 18:55:21 +03:00
Vitaliy Filippov 3a28149bad
Merge pull request #340 from jankatins/update_debian_version
Update debian packaging
2021-05-14 18:48:42 +03:00
Vitaliy Filippov e36f362abb
Merge pull request #342 from 0-wiz-0/master
Fix build on NetBSD
2021-05-14 18:46:36 +03:00
Thomas Klausner 30763e2f9d Add compatibility define for off64_t.
Not all systems define off64_t.
2021-04-15 12:41:40 +02:00
Thomas Klausner 814b724cc4 Handle NetBSD like macOS for timestamp precision. 2021-04-15 12:41:10 +02:00
Jan Katins 050fcf53a0 Move helper script into libexec dir
This seems to be a proper dir for such "not-for-normal-user" things...
2021-03-30 15:19:53 +02:00
Jan Katins 9076e3aed2 Ensure that the systemd files get installed in debian
If you build in pbuilder, no systemd is installed and so the flag is set to false which means the files will not get installed. Adding the explicit flag will make sure they are included in the package.
2021-03-23 18:10:10 +01:00
Jan Katins 27ca714e56 Add .idea folder to .gitignore 2021-03-18 10:34:15 +01:00
Jan Katins e0965f7fb8 Remove the libstdc++ dependency
These are included in the right version via the suggested installation of build-essential, which which installs the right lib via dependencies: g++ -> g++-<version> ->  libstdc++-<version>

Adding it explicitly meant always adding new versions  :-(
2021-03-18 10:33:44 +01:00
Jan Katins 5510ca1b06 More consistent directory name output in logs 2021-03-15 14:24:36 +01:00
Jan Katins 4dfc9a5461 Install grive-sync.sh into the right libdir 2021-03-15 14:12:31 +01:00
Jan Katins 378df2867b Do explicit unlocks in grive-sync.sh script 2021-03-15 13:30:46 +01:00
Jan Schulz 0b53b08f57 Increment version for debian package and add some changes there
* Updated debhelper compat level to latest stable one
2021-03-15 13:11:32 +01:00
Jan Katins d7008b81c1 Remove uneeded binutils-dev depency 2021-03-15 13:11:32 +01:00
Jan Schulz 1bab757298 Add systemd unit to enable both timer and changes based syncs
Now it's enough to enable and start `grive@<folder>.service`.
2021-03-14 22:49:59 +01:00
Jan Schulz c7a949dedd Revert "Exclude symlinks from sync"
This reverts commit b47dd70f35.

This could trigger a infinite loop between the timer unit (running a sync and changing the griveignore file at the start) and the changes unit (reacting to the griveignore change and retriggering the sync).
2021-03-14 22:46:43 +01:00
Agustin Alexander a8b84930f9
Optimized image size (compressed) 214MB -> 62MB 2020-09-23 19:32:04 -03:00
crborga 97a97265fd
Support Debian 10
Added support for building on Debian 10 without installing gcc7.
2020-04-07 21:35:28 -04:00
Michael Bartlett 7b42e2da5b fix systemd-escape hyphen usage 2020-03-03 13:34:27 -07:00
Vitaliy Filippov ffb744a59b
Merge pull request #285 from brainplot/cmake-fix
Add 'project' directive to the build script
2020-02-06 01:25:03 +03:00
Vitaliy Filippov 03bbc20892
Merge pull request #300 from redmercury/master
This change fixed a BSD compile issue.
2020-02-06 01:23:43 +03:00
Vitaliy Filippov 1456e6e0ba
Merge pull request #289 from danielsouzat/master
+clean debian packaging
2020-02-06 01:19:08 +03:00
Vitaliy Filippov 2ae61ab1e6
Merge branch 'master' into master 2020-02-06 01:18:57 +03:00
Vitaliy Filippov a295641cb0
Merge pull request #259 from Tatsh/mac
macOS fixes
2020-02-06 01:16:58 +03:00
Vitaliy Filippov 2ddc0230da
Merge branch 'master' into mac 2020-02-06 01:16:46 +03:00
Vitaliy Filippov 58a689a3db
Merge pull request #295 from fbicknel/patch-1
Fix Fedora (and RHEL) suggested dependencies
2020-02-06 01:14:05 +03:00
Vitaliy Filippov af2a6de268
Merge pull request #299 from cowo78/master
Support compilation with binutils >= 2.33.1 on Debian.
2020-02-06 01:12:17 +03:00
Vitaliy Filippov fa3c39480a
Merge pull request #303 from lmmamercy/master
fix ubuntu/debian suggested dependencies
2020-02-06 01:07:59 +03:00
lmmamercy dc1946498d
fix ubuntu/debian suggested dependencies 2020-02-05 14:30:34 +00:00
Daniel Deptford 6aeec25778 Adding CMake's Backtrace package to resolve issue where the project fails to compile under BSD. 2020-01-19 20:41:02 -08:00
Giuseppe Corbelli b4d6ac055f [SymbolInfo.cc] Support compilation with binutils >= 2.33.1 on Debian.
bfd_get_section_vma macro was removed.
bfd_section_size changed signature.

See inline comment for details.
2020-01-14 11:50:20 +01:00
Vitaliy Filippov a3cce71643 Small corrections 2020-01-06 20:37:32 +03:00
Vitaliy Filippov 37df02adff
Merge pull request #297 from AmonRaNet/update-readme
Add instruction for replacing the OAuth2 client
2020-01-06 20:22:37 +03:00
AmonRaNet 94182975db Add instruction for replacing the OAuth2 client 2019-12-31 20:13:26 +01:00
Frank Bicknell de6404b246
Fix Fedora (and RHEL) suggested dependencies
We need boost-devel to compile.
2019-12-16 13:27:43 -05:00
Vitaliy Filippov bdcb76efc0
Merge pull request #294 from AmonRaNet/extend-custom-app-id-secret
Extends support application id and secret support
2019-12-15 23:58:57 +03:00
AmonRaNet 79312b9c53 Extends support application id and secret support
- persistent id's in config
- cmake supports id's during config
- priority: default(cmake), config, cli
2019-12-13 11:16:32 +01:00
daniel ad7844ba29 modified: README.md
modified:   debian/rules
2019-11-22 09:35:43 -03:00
Vitaliy Filippov 39299096cf Move to 0.5.2-dev 2019-11-13 01:06:03 +03:00
Vitaliy Filippov 548ea362f3 Update client_id and client_secret -> maybe Google will verify it? 2019-11-13 01:05:31 +03:00
Vitaliy Filippov 13ac9bd1a9 Add ACHTUNG note to the README 2019-11-11 21:10:40 +03:00
Gianluca Recchia fe17d715fc
Add 'project' directive to the build script 2019-10-30 17:37:11 +01:00
Vitaliy Filippov f039e38c4c
Merge pull request #221 from martinak1/master
Added the install instruction for Fedora
2019-02-24 18:24:38 +03:00
Vitaliy Filippov 294b1b1ded
Merge pull request #244 from jankatins/fix_unescaped_folder_name
Escape folder when calling sync from change listener
2019-02-24 18:22:13 +03:00
Vitaliy Filippov 00d5148c19
Merge pull request #247 from jankatins/remove-unneded-dependencies
Remove unneded link-time dependency on libz and libexpat
2019-02-24 18:09:23 +03:00
Jan Schulz d4742fd470 Remove unneded link-time dependency on libz and libexpat 2019-02-09 23:38:11 +01:00
Andrew Udvare 6354689a93 libgrive: fix formatting 2019-02-09 17:06:23 -05:00
Andrew Udvare 7ef50e9523 libgrive: Fix destructors of Feed and Feed2 2019-02-09 17:01:00 -05:00
Tatsh e972a77790
Fix line 2019-02-09 10:56:23 -05:00
Andrew Udvare 8e3c3d25cb Define off64_t=off_t for macOS
Detect the systemd binary and install the systemd units conditionally.
2019-02-09 01:33:39 -05:00
Jan Schulz f9cad3b635 Update readme how to enable and start systemd units
It missed that escaping was essential if you didn't use a all [a-z] foldername...
2018-12-05 00:10:41 +01:00
Jan Schulz e3e18fe16a Escape folder when calling sync from change listener
The old version would pass in an unescaped directory, which then got unescaped by the sync call.

In some cases it worked but resulted in foldername wold not matched reality (example: google-drive -> google/drive) and error out when checking if that folder exists.
2018-12-05 00:10:41 +01:00
Alex Martin 60ecd5b0ff
Changed the g++ package to gcc-c++
Fixed the Fedora install directions.
2018-10-08 13:36:37 -04:00
Vitaliy Filippov cf51167b55 Return lost * matching back - fixes #223 2018-08-19 23:58:17 +03:00
a martin d4a0445873 Added the install instruction for Fedora
Tested on Fedora 28
2018-08-07 15:04:26 -04:00
Vitaliy Filippov cbac85a8e5 Fix #218 - Impossible to ignore sub-subdirectory via griveignore
Now an example .griveignore will look like this:

**
!dir/subdir/subdir
2018-07-31 01:11:12 +03:00
Vitaliy Filippov 93cae255fa Fix #169 2018-07-28 23:48:46 +03:00
Vitaliy Filippov 98d5b92e5d Followup to fddabe760f - should fix #217 2018-07-28 23:04:01 +03:00
Vitaliy Filippov f516356d33
Merge pull request #196 from jankatins/systemd_files_readme
Systemd files readme
2018-07-23 02:49:14 +03:00
Vitaliy Filippov 34d4582688
Merge pull request #160 from redmercury/custom-id-secret
New command line options
2018-07-23 02:48:14 +03:00
Vitaliy Filippov 6fe5f0c89b
Merge pull request #177 from psfloyd/master
Fix for systemd services and script, so it works with directories with sapces in their names.
2018-07-23 02:46:47 +03:00
Vitaliy Filippov 6b03e244df
Merge pull request #179 from proski/c2-uninitialized
Fix uninitialized use of c2 variable in State::FromLocal
2018-07-23 02:43:31 +03:00
Vitaliy Filippov ad9b66a92c
Merge pull request #186 from cpu82/master
FreeBSD fixes and improvements
2018-07-23 02:40:23 +03:00
Vitaliy Filippov 8384f0c983
Merge pull request #180 from proski/boost-only-needed-libraries
Link against specific Boost libraries, avoid using Boost_LIBRARIES
2018-07-23 02:39:34 +03:00
Vitaliy Filippov 59499327e2
Merge pull request #201 from TopherGopher/master
Drastically increases page sizes for listing - Dockerizes grive2 (cross-platform)
2018-07-23 02:39:02 +03:00
psfloyd b47dd70f35 Exclude symlinks from sync 2018-05-12 23:12:45 -03:00
Topher Sterling f19406e57d Reduces the size of the Docker image. 2018-04-07 21:50:20 -06:00
Topher Sterling f0e38924d0 Reduces the size of the Docker image. 2018-04-07 21:41:36 -06:00
Topher Sterling 1443dd9f18 Adding dumb-init. 2018-04-07 20:40:55 -06:00
Topher Sterling f5e1b67b4c Why not just go ahead and run the executable on entry? 2018-04-07 20:34:37 -06:00
Topher Sterling 48bfe66bab Dockerfile will attempt to build the binary now. 2018-04-07 20:03:42 -06:00
Topher Sterling fddabe760f Upping maxResults limits. 2018-04-07 19:56:11 -06:00
Topher Sterling 80bbe5f940 Adding dockerfiles for isolated build. 2018-04-07 19:55:39 -06:00
Jan Katins 2de114b212 Add systemd file install to README 2018-02-02 21:03:34 +01:00
Jan Katins cdea48edc1 gitignore: ignore files produced by the deb build 2018-01-30 22:04:59 +01:00
Carlos J b06f66c86d Unbreak the build against new Clang version 6.0.0 2018-01-15 23:43:15 +01:00
Carlos J bff462211a FreeBSD fixes and improvements 2018-01-15 21:59:17 +01:00
Pavel Roskin 8f2f217da6 Link against specific Boost libraries, avoid using Boost_LIBRARIES
grive should not be linked against the Boost unit test framework, only
btest needs it.

Don't specify Boost libraries for executables if libgrive uses those
libraries. CMake takes care of the dependencies.
2018-01-02 18:15:16 -08:00
Pavel Roskin 2e75fc669b Fix uninitialized use of c2 variable in State::FromLocal 2018-01-02 16:27:41 -08:00
psfloyd d698d596d9 Can enable services systemd excaped 2017-12-08 00:23:17 -03:00
psfloyd 94bda77b1b Removed debug line 2017-12-04 02:33:53 -03:00
psfloyd 70c5c64373 Fix for service files and script to work with directories with spaces in their names 2017-12-01 00:12:14 -03:00
Vitaliy Filippov 84c57c121e
Merge pull request #115 from sizeofvoid/master
OpenBSD install and compile fixes and improvements
2017-11-21 23:50:42 +03:00
Vitaliy Filippov 2155b755c4
Merge pull request #170 from vlachoudis/master
Added libstdc++-7-dev in the Build-Depends for Ubuntu 17.10
2017-11-21 23:50:13 +03:00
Vitaliy Filippov 76d76005ff
Merge pull request #122 from aschei/master
Fix man page to reflect fix #58
2017-11-21 23:49:49 +03:00
Vitaliy Filippov dcbdbfcd1e
Merge branch 'master' into master 2017-11-21 23:49:36 +03:00
Vitaliy Filippov 262edd71cc
Merge branch 'master' into master 2017-11-21 22:38:55 +03:00
Vitaliy Filippov d84e0d9b9c
Merge pull request #143 from janschulz/systemd_grive
Add user systemd units
2017-11-21 22:35:55 +03:00
Vitaliy Filippov 4e5c61b668 Add pkg-config (fixes #126) 2017-11-21 22:34:46 +03:00
Vitaliy Filippov 5561c0e847
Merge pull request #132 from doronbehar/master
Add completion file for zsh.
2017-11-21 22:33:16 +03:00
Vasilis 81a7c34c22 Added libstdc++-7-dev in the Build-Depends for Ubuntu 17.10 2017-11-07 17:39:13 +01:00
Daniel Deptford 31b5ab59fc Adding command-line options to specify authentication id and secret.
On branch custom-id-secret
Changes to be committed:
	modified:   grive/src/main.cc
2017-10-13 22:25:51 -07:00
Vitaliy Filippov 3c90425b8f Merge pull request #155 from mitoskalandiel/patch-1
added 3 more dependencies
2017-10-05 00:38:45 +03:00
Mitos Kalandiel 90c603dc4c added 3 more dependencies
on linux mint 18.3 I also had to install "debhelper", "zlib1g-dev" & "dpkg-dev" to be able to run the dpkg-buildpackage command
2017-08-11 12:05:53 +01:00
Vitaliy Filippov 4fe1e71d5b Issue #148 - add debug message for the case 2017-06-05 14:19:47 +03:00
Jan Schulz d996989c29 Add user systemd units
The units can be used to automatically sync a folder in the users home dir
* every 3 minutes
* on local file changes (requires inotify-tools).

To enable the units for `~/google-drive/`:

```
systemctl --user enable grive-timer@google-drive.timer
systemctl --user start grive-timer@google-drive.timer
systemctl --user enable grive-changes@google-drive.service
systemctl --user start grive-changes@google-drive.service
```

The units can be enabled multiple times
2017-04-27 23:18:54 +02:00
Vitaliy Filippov 4b6cf69cbb Fix #139 2017-03-22 18:28:48 +03:00
Vitaliy Filippov 63bb138b2d Fix #120, fix #125, fix #134 - Add .griveignore 2017-03-19 20:00:44 +03:00
Vitaliy Filippov a0aff5b146 Fix #136 - Skip links, sockets, fifos and etc 2017-03-19 12:48:01 +03:00
Vitaliy Filippov e8a8801c75 Merge pull request #137 from JanST123/old_curl
also compile with cURL Version less than 7.32.0
2017-03-19 13:07:54 +04:00
Jan 974733ff46 also compile with cURL Version less than 7.32.0 2017-03-10 13:33:40 +01:00
Doron Behar bddf8cf6b0 Add completion file for zsh. 2017-02-19 20:03:37 +02:00
Albrecht Scheidig f8ea376769 Fix man page to reflect fix #58
"Make ignore regexp non-persistent (fix #58)" should be reflected in the
man page.
2016-12-06 23:28:28 +01:00
Rafael Sadowski 150a817628 fix: man(1) install directory unter OpenBSD 2016-10-09 11:00:12 +02:00
Rafael Sadowski 32f62c0850 fix: missing include under OpenBSD 2016-10-09 10:54:35 +02:00
Vitaliy Filippov 7bbb01c301 Fix #108 :)) 2016-10-04 12:25:24 +03:00
Vitaliy Filippov 04b86d1c87 :D:D:D fix #106 2016-10-01 15:24:13 +03:00
Vitaliy Filippov 08e29070c2 Fix possible uninit m_pb, print \33[K (clreol) in the end for less glitches in xfce4-terminal and \r for better window resize 2016-10-01 02:09:33 +03:00
Vitaliy Filippov e3f948496d Fix #105 2016-10-01 01:49:30 +03:00
Vitaliy Filippov 68e0e5afe5 Get progressbar::last back, do not print multiple 100% lines (fixes #104) 2016-09-30 12:33:11 +03:00
Vitaliy Filippov 67b5b05e17 Fix auth segfault, remove 1/1000 progressbar interval (fixes #103) 2016-09-30 01:14:58 +03:00
Blackrabbit f27e3724de Implement progressbar (by @svartkanin adjusted by @vitalif) 2016-09-29 00:56:15 +03:00
Vitaliy Filippov 11a3d788d0 Fix libgrive/test build after removing doclist api (fixes #102) 2016-09-28 10:16:57 +03:00
Vitaliy Filippov dd77c99872 Check MD5 only when it is needed (fixes #101)
This makes grive check md5 sums only when:
1) a local rename is supposed (when there are a new file and a deleted file of the same size)
2) local ctime is changed, but file size isn't
2016-09-28 01:27:28 +03:00
Vitaliy Filippov 59d02a65cb Remove outdated Doclist API implementation 2016-09-28 01:01:50 +03:00
Vitaliy Filippov d35c849468 add std=c++0x for older gcc (fixes #96) 2016-09-02 13:47:14 +03:00
Vitaliy Filippov 199a050099 Change deprecated auto_ptr to unique_ptr 2016-09-01 16:19:14 +03:00
Vitaliy Filippov 195e5091c7 Add debian package note to readme 2016-08-29 13:29:19 +03:00
Vitaliy Filippov a756414e71 Skip unreadable files during sync, remove extra stat calls (fix #63) 2016-07-31 22:05:18 +03:00
Vitaliy Filippov e91a2b598b Name it working copy, not just root (fixes #86) 2016-07-27 17:21:35 +03:00
Vitaliy Filippov 457d849745 Update package files 2016-07-13 21:13:52 +03:00
Vitaliy Filippov fbf8f1663f Initialize mMaxUpload/mMaxDownload to 0 (fix #81) 2016-07-06 12:04:15 +03:00
Vitaliy Filippov 2727160257 Note that ignore regexp and subdir are not persistent anymore (sorry :)) 2016-06-30 13:18:25 +03:00
Vitaliy Filippov 76827a760c Allow libstdc++-6-dev 2016-06-30 13:16:46 +03:00
Vitaliy Filippov 46dfa1abfa Add boost_INCLUDE_DIRS (fix #77) 2016-05-21 12:45:43 +03:00
Vitaliy Filippov 1bd86307c6 Allow 5 params in Log, fix typo 2016-05-16 12:29:29 +03:00
Vitaliy Filippov 44d3ddf928 Implement upload/download speed limit (fix #60) 2016-05-15 23:05:49 +03:00
Vitaliy Filippov 5327016d36 Make ignore regexp non-persistent (fix #58) 2016-05-11 15:56:59 +03:00
Vitaliy Filippov 62e26118f2 Fix #62 - sub-subdirectory sync with -s 2016-05-10 23:10:45 +03:00
Vitaliy Filippov 40e33cb524 Implement upload-only and no-remote-new modes (fix #69) 2016-05-10 00:26:10 +03:00
Vitaliy Filippov cfb8ff08b3 Move to 0.5.1-dev 2016-01-14 23:51:31 +03:00
Vitaliy Filippov ae06eccb38 Release 0.5 2016-01-14 23:51:31 +03:00
Vitaliy Filippov 98416354f7 Remove "me in readers" condition (fix #37) 2016-01-11 18:15:29 +03:00
Vitaliy Filippov 86acd18978 Fix ResourceTest 2016-01-06 12:55:46 +03:00
Vitaliy Filippov 4a4e22026b Fix json Val type check 2016-01-06 02:21:33 +03:00
Vitaliy Filippov b6c0013052 Fix subdir/subsubdir/newfile bug
New files put in sub-subdirs of drive root were also trashed locally instead of begin uploaded to the server
2016-01-06 01:48:30 +03:00
Vitaliy Filippov 60acb75967 Fix local index updating for items already in sync 2016-01-06 01:44:22 +03:00
Vitaliy Filippov c76cdecad2 Note fixed revision support in readme 2016-01-05 20:27:52 +03:00
Vitaliy Filippov 0112330c1d Do not die and do not update local state of file on sync exceptions. Should fix #47 2016-01-05 20:14:35 +03:00
Vitaliy Filippov d606a360be Merge pull request #54 from Artox/pr
std::vector::erase is expensive; use std::list:erase instead
2016-01-03 19:24:39 +03:00
Vitaliy Filippov 9d8c77d0bd Update manpage 2016-01-03 18:24:34 +03:00
Vitaliy Filippov 3b9aa4f2aa Add new_rev switch support to drive v2 api client 2016-01-03 18:24:34 +03:00
Vitaliy Filippov ca4a0b6b80 Index allows us to not turn -f on when changing ignore regexp 2016-01-03 17:40:23 +03:00
Josua Mayer b49a89ad34 std::vector::erase is expensive; use std::list:erase instead
erasing from a vector at random is very expensive. std::list however is made for that purpose!
2016-01-03 14:24:55 +01:00
Vitaliy Filippov c647c5f89f Update debian package version 2016-01-03 12:51:38 +03:00
Vitaliy Filippov 00311e8365 Implement automatic rename detection 2016-01-03 12:43:22 +03:00
Vitaliy Filippov 23fa985bdb Do not track last_sync at all, clear srv_time's with -f option 2016-01-03 02:01:02 +03:00
Vitaliy Filippov 8f640ebdad Check local index instead of last_sync time, totally disable separate folder and changes sync passes 2016-01-03 02:01:02 +03:00
Vitaliy Filippov af05c7c626 Use stored server times to check for server-side modifications
(broken because deleted/new detection logic relies on last_sync/last_change)
2016-01-03 02:01:02 +03:00
Vitaliy Filippov 4edff0a816 Cache file checksums and modification times in local index (in JSON in .grive_state) 2016-01-03 02:01:01 +03:00
Vitaliy Filippov 5381919e5b Enable warnings and fix them 2016-01-03 02:01:01 +03:00
Vitaliy Filippov d2a6105603 Release 0.4.2 2016-01-03 02:01:01 +03:00
Vitaliy Filippov 5fb3c187fe Oops this was a fail. ignore option was ignored if it was previously empty in grive_state. Fixes #50 2015-12-28 02:00:47 +03:00
Vitaliy Filippov ff9d7bcd14 Warn, but not die, on filesystem exceptions during sync. Fixes #44 2015-12-13 01:53:35 +03:00
Vitaliy Filippov c0fadcffe3 State that -s and --ignore are remembered for next runs of Grive in --help 2015-12-12 17:22:17 +03:00
Vitaliy Filippov 9c43ad9cc9 Internally convert -s option to ignore regexp. Fixes #49, should fix #42. 2015-12-12 17:19:07 +03:00
Vitaliy Filippov 7e6e153a3a Use regex_search() instead of regex_match(), turn on -f implicitly when changing ignore regexp (should fix #43) 2015-12-12 17:19:00 +03:00
Vitaliy Filippov 85b35b4fca Fix RelPath() for --ignore option to work correctly 2015-12-12 16:44:02 +03:00
Vitaliy Filippov cc13b8b343 Fix finding libbfd on newer Debian 2015-12-09 01:41:04 +03:00
Vitaliy Filippov 41bf5ba845 "Fix" file size at the moment of adding it to ConcatStream (fixes #39) 2015-11-11 13:08:08 +03:00
Vitaliy Filippov afbd9d7234 Merge pull request #40 from theopolis/check_downloadurl
Check for missing downloadUrl
2015-11-11 12:30:28 +03:00
Vitaliy Filippov 94e24d8d55 Add required package lists into README 2015-11-11 12:31:36 +03:00
Vitaliy Filippov 450ff5857e Add move/rename to README 2015-11-10 12:58:55 +03:00
Dylan Wulf 3b608329b9 Added ability to move and rename files/directories 2015-11-10 12:54:49 +03:00
Teddy Reed 9156785334 Check for missing downloadUrl 2015-11-09 20:27:55 -08:00
Vitaliy Filippov 44cb91f94e State that YAJL 2.x is required 2015-10-11 18:59:25 +03:00
Vitaliy Filippov f0b6bcf8b4 Add Debian packaging scripts
Based on work by Alin Andrei (https://launchpad.net/~nilarimogard/+archive/ubuntu/webupd8)
2015-10-11 15:32:00 +03:00
Vitaliy Filippov 81b1a4f05c Fix non-virtual destructor and missing return value (fixes "illegal instruction" under FreeBSD) 2015-10-10 21:43:54 +00:00
Vitaliy Filippov df99954382 Change m_str.replace back to insert + remove StringStream limit 2015-10-10 13:01:05 +03:00
Vitaliy Filippov 887da88c14 Simplify http::Agent methods and use multipart upload
Should speed-up uploads by updating metadata and contents at the same time.
Also should fix most 412 errors because of the update atomicity.
2015-10-10 00:54:05 +03:00
Vitaliy Filippov 679fa0eec6 Use title instead of originalFilename - fixes #32 2015-10-08 14:23:59 +03:00
Vitaliy Filippov 1cca10272d Track both server-side and local sync times - fixes #6 2015-10-08 13:30:50 +03:00
Vitaliy Filippov d9300c953e Fix .trash and .grive_state in drive location when used with -p option
Previously .trash and .grive_state were incorrectly placed in the current
working directory instead of synced root directory (-p).
2015-10-08 01:41:35 +03:00
Vitaliy Filippov f288c559c6 Oops. I was thinking auto_ptr is smart_ptr O_o. Sorry. Also fix #33 2015-10-07 01:57:28 +03:00
Vitaliy Filippov dc8e172a27 Handle error HTTP responses in OAuth2 2015-09-30 15:52:24 +03:00
Vitaliy Filippov 9402bff12e Implement normal reliable HTTP logging (in Agent)
Flushes on every call so may be slightly slow, but in return always provides full
information in case of a segfault or a failed assertion.
2015-09-30 15:01:50 +03:00
Vitaliy Filippov 9b8e0c826b Remove request logging from Drive/Feed/Syncer (prepare to move it into CurlAgent) 2015-09-30 13:54:45 +03:00
Vitaliy Filippov 8fd74d7a36 Update readme 2015-09-25 13:27:54 +03:00
Vitaliy Filippov 7ac15187d3 Add --ignore (perl regexp) option 2015-09-25 13:11:56 +03:00
Vitaliy Filippov 5dc7028c8a Add expat into README.md dependency list - closes #25 2015-08-30 22:48:30 +03:00
Vitaliy Filippov 6f35a32ca2 Get auth headers again before each Put() retry - fixes #19 2015-08-04 22:59:09 +03:00
Vitaliy Filippov 308aab92cf Also print response body on 401 2015-07-04 00:25:13 +03:00
Vitaliy Filippov 1d47616518 Fix #17 (curl_slist memory leak) 2015-07-04 00:18:59 +03:00
Vitaliy Filippov 5da32905ea Move old tests to fix build errors, fixes #14 2015-06-27 23:18:03 +03:00
Vitaliy Filippov 70ec926e2a Add response body and headers to diagnostic information on HTTP 400 and above
Also should fix #13 because stops AuthAgent from using the same response multiple times
2015-06-27 22:58:09 +03:00
Vitaliy Filippov 06eb1a7df2 Fix FreeBSD build 2015-05-31 22:35:15 +03:00
Vitaliy Filippov 82705f39b0 Fix 412's on reupload. Fixes #5 2015-05-21 19:23:45 +03:00
Vitaliy Filippov 37b2cf3102 Ignore files without md5Checksum. Fixes #2 2015-05-20 15:14:24 +03:00
Vitaliy Filippov 815d0ea44e Make libbfd happy. In conjunction with libiberty fixes #1 2015-05-20 02:15:01 +03:00
Vitaliy Filippov c006e75d26 Return libiberty back 2015-05-20 02:15:01 +03:00
Vitaliy Filippov 29241002eb Add myself to copyrights, change README 2015-05-17 22:11:35 +03:00
Vitaliy Filippov 0bdbf87514 Ignore local .trash, fix REST API - it mostly works 2015-05-17 21:05:24 +03:00
Vitaliy Filippov 2d34d7708b Implement upload methods for REST API Syncer (basic file upload to root now works) 2015-05-17 20:10:03 +03:00
Vitaliy Filippov a4521d9d62 Remove obsolete gdbm & iberty cmake modules 2015-05-17 16:59:58 +03:00
Vitaliy Filippov 862fca035a Move all protocol interaction into Syncers out of the base code 2015-05-17 16:54:04 +03:00
Vitaliy Filippov 717a6a4793 Use vector<Entry> instead of iterator_adaptor to hide implementation details 2015-05-17 14:32:04 +03:00
Vitaliy Filippov dabaaac38f Cleanup Feed interface (remove some unused methods) 2015-05-17 12:29:00 +03:00
Vitaliy Filippov 6a15dd09a5 Split Resource to global (base/Resource) and API-specific ({drive,drive2}/Syncer) parts 2015-05-17 02:00:00 +03:00
Vitaliy Filippov f41e4f6dd3 Split Entry to v1::Entry1 and v2::Entry2 2015-05-16 21:10:38 +03:00
Vitaliy Filippov b0255d9699 Add Entry constructor for new Drive REST API 2015-05-16 19:38:43 +03:00
Vitaliy Filippov 2d8da5cab3 Remove doclist API specific fields from Entry class: kind, create_link, edit_link and alt_self 2015-05-16 12:35:41 +03:00
Vitaliy Filippov 770baca3fc Reduce syscalls slightly more (os::FileCTime is still left...) 2015-05-16 01:05:19 +03:00
Vitaliy Filippov 5bc503279a Remove JSON-C json wrappers, use YAJL for everything 2015-05-16 00:37:02 +03:00
Vitaliy Filippov ac1763f2c7 Merge https://github.com/visap/grive/commits/visa 2015-05-16 00:36:26 +03:00
Vitaliy Filippov 12774a952a Remove bgrive (dead and unused code) 2015-05-14 01:22:37 +03:00
Vitaliy Filippov f16f212281 use pkg-config also for finding json-c (fixes paths on debian) 2015-05-14 01:20:35 +03:00
Vitaliy Filippov c0c89c306c Remove useless swap() code 2015-05-14 01:10:09 +03:00
Vitaliy Filippov 94efea11dd Do not try to install doc/grive.1 from bgrive cmake 2014-07-23 14:51:13 +04:00
Vitaliy Filippov 400d9e0c0f Fix long/int cast errors 2014-07-23 14:51:13 +04:00
Vitaliy Filippov 459313ecf5 Find YAJL via pkg-config 2014-07-23 14:51:13 +04:00
Visa Putkinen 3775572f46 Retry upload on XML error instead of crashing
Sometimes the Google Drive API sends malformed XML which crashes grive.
This patch adds a simple try-catch to Resource::Upload that retries the
upload if an XML exception is thrown from AuthAgent::Put.
2013-11-27 00:15:24 +02:00
Visa Putkinen 84785ec473 Fix hang when upload receives HTTP 500
When an uploading PUT request got a HTTP 500 as reponse, grive hanged
forever inside libcurl. This was because the File parameter was not
rewound to 0 position on retry. The XmlResponse had to be cleared as
well.

Rewinding the File and clearing the XmlResponse were not enough to fix
the problem, because when retrying after 500, HTTP 410 Gone or 412
Precondition failed is often received, and CheckHttpResponse would throw
an exception that crashes grive. Therefore, I implemented a retry logic
to Resource::Upload that retries the whole upload transaction if 410 or
412 was received.
2013-11-27 00:15:24 +02:00
Visa Putkinen 645bb2e7d4 Add delay before auth token refresh
This is sometimes necessary to prevent too frequent requests.
2013-11-27 00:15:15 +02:00
Visa Putkinen 986ab4acc6 Log XML on parse failure 2013-11-25 00:18:47 +02:00
Visa Putkinen 8b434dad14 Reduce number of syscalls while scanning local files 2013-11-24 14:34:03 +02:00
Visa Putkinen c15bfbe8bc Ignore only .grive and .grive_state 2013-11-24 14:25:01 +02:00
Visa Putkinen 89506b4162 Add autogenerated stuff to .gitignore 2013-11-24 14:22:49 +02:00
Vitaliy Filippov 752eb3fdda Allow to sync just one directory in the root 2013-09-08 02:36:30 +04:00
Nestal Wan 27817e835f using the new Json classes for bgrive 2013-05-03 00:40:04 +08:00
Nestal Wan 6ba04dcfba added operator<< to print out json 2013-05-02 00:55:39 +08:00
Nestal Wan 6fa6e690c6 added json writer 2013-05-02 00:41:47 +08:00
Nestal Wan abfa9ce765 added ValResponse class to parse JSON from http 2013-05-02 00:04:42 +08:00
Nestal Wan 2d29692601 added yajl parser 2013-05-01 22:47:37 +08:00
Nestal Wan 540e3d82db added type conversion to supported types 2013-05-01 13:36:29 +08:00
Nestal Wan 658fb757e3 added Val class for generic representation of JSON, but there's some problems with maps 2013-05-01 01:56:34 +08:00
Nestal Wan d5ca8e0afa show title and mime in GUI 2013-04-30 00:13:13 +08:00
Nestal Wan 15b3b85807 UT compile fixes 2013-04-29 23:49:01 +08:00
Nestal Wan a668b7670b added ui on the right. no logic yet 2013-04-29 23:47:33 +08:00
Nestal Wan bba65c0cc7 show files now. working good. 2013-04-29 23:47:33 +08:00
Nestal Wan 93fe68ce4c fixing the problems with multiple parent 2013-04-29 23:47:33 +08:00
Nestal Wan 94d66f0d30 can show tree now, but still has issues 2013-04-29 23:47:33 +08:00
Nestal Wan 326ae20ca7 showing in qt model, but no child 2013-04-29 23:47:33 +08:00
Nestal Wan ee6408d05f changed to store parent IDs 2013-04-29 23:47:33 +08:00
Nestal Wan 87d96972f7 Added Query() helper to Feed. and common URI header 2013-04-29 23:47:33 +08:00
Nestal Wan 209d0b5a59 added code to fetch all folders 2013-04-29 23:47:33 +08:00
Nestal Wan 62dc542341 refactored old GDoc API into v1 namespace. using v2 namespace for new Google Drive API 2013-04-29 23:47:33 +08:00
Nestal Wan 738435837b using DataStream instead of Receiveable 2013-04-29 23:47:32 +08:00
Nestal Wan 443a85d558 removing redundant header parameter in Feed 2013-04-29 23:47:32 +08:00
Nestal Wan d4a7fae273 refactored exception handling 2013-04-29 23:47:32 +08:00
Nestal Wan 39b2b4fb0b added DriveModel. just skeleton. 2013-04-29 23:47:32 +08:00
Nestal Wan 755ee1c9ad new qt gui 2013-04-29 23:47:32 +08:00
Nestal Wan 113b1107d8 Merge pull request #112 from justinharding/issue_86_partial_sync
fix broken unit tests
2012-08-06 07:51:57 -07:00
justin 29f17397c1 fix broken unit tests 2012-08-01 20:46:59 +01:00
Nestal Wan 804fe923f2 refactored the config file part from #109 2012-07-30 16:52:59 +08:00
Nestal Wan 612b35a090 fixed compiler warnings 2012-07-30 16:43:51 +08:00
Nestal Wan bec8362639 added template contructor for object 2012-07-30 16:43:36 +08:00
Nestal Wan 0b2a8c0a54 clean up on #109 2012-07-30 15:24:47 +08:00
Nestal Wan cb602052f1 Merge pull request #109 from justinharding/issue_86_partial_sync
Issue 86 partial sync
2012-07-29 21:56:02 -07:00
justin 034891a610 issue 86 allow sync folder to be specified, move Config class and add basic unit test 2012-07-29 18:17:41 +01:00
justin e0383a843c remove unused code 2012-07-29 17:53:02 +01:00
Nestal Wan d831b8d59f support creating revisions with --new-rev (#87) 2012-07-27 00:45:53 +08:00
Nestal Wan fd81cc1fe4 added --log-xml for debugging (#80) 2012-07-27 00:17:44 +08:00
Nestal Wan e375d92b82 version changes for v0.3 2012-07-26 01:29:47 +08:00
Nestal Wan 5a0c6ae56e don't throw in CurlAgent for all HTTP errors (#82) 2012-07-26 01:25:44 +08:00
Nestal Wan 22fd859be6 Merge pull request #105 from stephencox/master
README typo
2012-07-25 09:30:52 -07:00
Stephen Cox 2e9761e58e Removed duplicate "considered" in README 2012-07-23 11:09:53 +02:00
Nestal Wan 51e42914f3 refreshing the token if HTTP 401 is returned (#83) 2012-07-23 16:00:21 +08:00
Nestal Wan 3acd25cb55 proper fix for (#98: missing reference count for Json::Get() ) 2012-07-23 15:43:11 +08:00
Nestal Wan 271e4d7ed8 Merge pull request #100 from justinharding/mtimebranch
store modified time of file in m_mtime instead of passing value as param
2012-07-23 00:21:58 -07:00
justin 96227d38cf store modified time of file in m_mtime instead of passing value as param 2012-07-19 22:22:34 +01:00
Nestal Wan c6a1c47d40 added retry on HTTP 500 & 503 (#82) 2012-07-20 00:50:40 +08:00
Nestal Wan 28e8012ca5 added AuthAgent, a wrapper around http agent 2012-07-20 00:39:36 +08:00
Nestal Wan 57c431dd83 added logs for tracing last sync time 2012-07-19 23:48:01 +08:00
Nestal Wan 28e4956af9 Merge pull request #99 from fairlight1337/master
Removed linking to IBERTY_LIBRARY and added Boost to CMakeLists file
2012-07-19 08:40:27 -07:00
Jan Winkler 2f94168599 Set up an else-clause for IBERTY_LIBRARY and removed unnecessary BOOST-find
In ./CMakeLists.txt the added find_package( Boost ) was removed due to being unnecessary. In libgrive/CMakeLists.txt the IBERTY_LIBRARY is now set to an empty string of it was not found (otherwise the linker breaks during `cmake').
2012-07-19 17:19:40 +02:00
Jan Winkler 56de93c8c7 Commented out iberty_library stuff
The iberty_library could not be found anywhere in my repos or on my system. Compiles and runs fine without.
2012-07-19 16:51:03 +02:00
Nestal Wan 7cc4984932 use mmap for checksum 2012-07-17 01:59:00 +08:00
Nestal Wan ce245576b5 use StdioFile for uploading (#88) 2012-07-17 01:58:16 +08:00
Nestal Wan 75c67dad8d added class & functions for memory mapped files 2012-07-17 01:08:34 +08:00
Nestal Wan 94383e0030 ignoring files with missing MD5 checksum in remote feeds 2012-07-16 23:37:50 +08:00
Nestal Wan 593c335a5e improved logging for HTTP responses 2012-07-16 23:23:08 +08:00
Nestal Wan 394982da4e added _FILE_OFFSET_BITS=64 (#88) 2012-07-16 22:30:30 +08:00
Nestal Wan 8547bbf7ab Merge pull request #92 from justinharding/master
sending a pull request
2012-07-16 06:54:46 -07:00
justin e28fe96260 fix deleted local file downloaded again 2012-07-15 17:33:33 +01:00
Nestal Wan 80f3fbf370 refactored the logs to debug sync problems 2012-07-12 00:36:58 +08:00
Nestal Wan 278037b07b fixed incorrect name in pdf files 2012-07-12 00:30:19 +08:00
Nestal Wan 66b3d37872 README: beta quality 2012-07-11 22:56:28 +08:00
Nestal Wan af68bde44b Merge pull request #89 from alerque/patch-1
Minor English cleanup.
2012-07-11 07:27:05 -07:00
Caleb Maclennan 580b2f67f2 Minor English cleanup. 2012-07-11 12:08:42 +03:00
Nestal Wan 93d696a527 Merge pull request #84 from Nightgunner5/empty-filenames
Use the "title" for google documents
2012-07-07 08:43:18 -07:00
Ben L 06bf4fe58d Fix empty filenames for Google Docs 2012-07-03 13:42:16 -05:00
Nestal Wan f35a346a0f fixed web site (#81) 2012-07-03 00:54:18 +08:00
Nestal Wan 34cb370528 fixed link to wiki (#78) 2012-06-28 00:19:51 +08:00
Nestal Wan daa1fe9ca3 refactored a bit for the fix in #76 2012-06-27 23:18:12 +08:00
Nestal Wan dc89aee613 Merge pull request #77 from madjar/master
Fixed a bug where grive crashed on the first run.
2012-06-27 07:12:16 -07:00
Georges Dubus 34be2e7a87 Fixed a bug where grive crashed on the first run.
It gived a negative start index to get the changes feed, which is
illegal. The error returned by the server triggered a crash.

Fixes #76.
2012-06-27 14:17:13 +02:00
Nestal Wan 460a4fd8f2 save the changestamp after sync (#68) 2012-06-26 00:13:18 +08:00
Nestal Wan cfbcd31830 Merge pull request #75 from georgwiltschek/master
Make grive compile on Ubuntu 10.04 (Lucid Lynx)
2012-06-25 08:47:03 -07:00
georg wiltschek 6ecbd8d0ea depending on libboost 1.40 seems no worse than on 1.42 but makes compiling much easier on ubuntu 10.04 :-) 2012-06-25 09:59:22 +02:00
Nestal Wan 1d1365ea1a refactored code in Update() to DetectChanges() 2012-06-23 18:23:43 +08:00
Erik Hardesty 62238c5ba6 Move update logic out of Grive constructor
This is to make it easier to repeatedly sync the
directory when used in a daemon-like setting without
reinstantiating the class
2012-06-21 18:47:01 -05:00
Nestal Wan 6850785667 added zlib for SUSE (#63) 2012-06-21 23:39:53 +08:00
Matchman Green f4b3e488ee removed Matchman from the man page 2012-06-20 23:02:22 +08:00
Nestal Wan 7158ce2538 added rules for installing the man page 2012-06-20 22:57:23 +08:00
Nestal Wan c5a5221f4b disable an unimportant assertion and replace it with a warning 2012-06-20 22:46:17 +08:00
Nestal Wan a799e7a611 fixed boost fs v2 compile error found by pavel.savara 2012-06-20 22:02:42 +08:00
Nestal Wan b906d90faf Merge branch 'jose' 2012-06-20 21:57:59 +08:00
José Luis Segura Lucas 78239913e1 Added manpage for Grive 2012-06-20 01:42:40 +02:00
Nestal Wan cf7f28174c preparing for the v0.2.0 release 2012-06-19 00:12:40 +08:00
Nestal Wan 55c0f4e6d5 mark folder and its children as created/deleted recursively (#61) 2012-06-18 23:44:28 +08:00
Nestal Wan 023c208d85 fixed assertion on dry-runs 2012-06-18 23:17:28 +08:00
Nestal Wan dfe89b19b9 don't install the headers when "make install" (#67) 2012-06-18 22:39:43 +08:00
Nestal Wan e388d0bf34 added mock agents for unit tests 2012-06-17 16:34:57 +08:00
Nestal Wan 48391bb16f added more log for dry-run (#65) 2012-06-17 16:33:38 +08:00
Nestal Wan 55ac367255 escape the directory names when uploading (#66) 2012-06-17 15:58:27 +08:00
Nestal Wan c4815da85c support for dry-run (#65) 2012-06-17 15:57:47 +08:00
Nestal Wan 277274f026 fixed handling on changes feed. 2012-06-15 00:55:00 +08:00
Nestal Wan dbf0ae4be0 used boost::program_option to manage command line options (#45) 2012-06-15 00:04:17 +08:00
Nestal Wan ea4fda5b54 fix recursively add folder (#59) 2012-06-14 01:42:41 +08:00
Nestal Wan 5740d7b77f cherry-picked commits from petrj (#60) 2012-06-12 23:53:32 +08:00
Nestal Wan 72052edf37 removed signal handler 2012-06-12 01:24:27 +08:00
Nestal Wan a79edc9c2b merged compile fixes from LemonBoy #58 2012-06-11 23:37:15 +08:00
Nestal Wan cf2c827a6e Revert "Config dirs is now XDG compliant."
This reverts commit 9a76cb8839.
2012-06-11 23:32:57 +08:00
LemonBoy 9a76cb8839 Config dirs is now XDG compliant. 2012-06-10 20:15:40 +02:00
LemonBoy 6157dc57f3 Fix some compilation issues. 2012-06-10 19:50:26 +02:00
Nestal Wan b0c0353fdb seems like the changes handling is still not perfect 2012-06-11 00:52:04 +08:00
Nestal Wan 8d822e3582 now sync'ing folder is the same as sync'ing files 2012-06-11 00:40:56 +08:00
Nestal Wan 991d8e444f recursively sync the resources 2012-06-10 23:38:27 +08:00
Nestal Wan 7be193c35b added unit test case 2012-06-10 23:11:32 +08:00
Nestal Wan ba8cbc18e8 libiberty is required by ubuntu as well (thanks chirayudesai) 2012-06-10 21:15:59 +08:00
Nestal Wan f0ce00ca7b improved support for changes feed 2012-06-10 21:13:47 +08:00
Nestal Wan 55572c7fc4 refactored. Resource does not contain Entry 2012-06-10 21:13:46 +08:00
Nestal Wan a863939b05 "fixed" UT 2012-06-10 21:13:46 +08:00
Nestal Wan 5f61be489b check for broken link when iterating directory (#55) 2012-06-10 21:12:32 +08:00
Nestal Wan 89e07bb257 removed OpenSSL in Download.cc 2012-06-10 15:36:37 +08:00
Nestal Wan b34e41c3a5 starting the 0.2.0 branch 2012-06-10 11:12:34 +08:00
Nestal Wan 6d50a0fdce updated RPM spec to point to latest version 2012-06-09 15:32:54 +08:00
Nestal Wan 2b6d195924 updated README for v0.1.1 2012-06-09 15:14:26 +08:00
Nestal Wan 948706530f preparing for v0.1.1 release 2012-06-09 14:48:35 +08:00
Nestal Wan 6aaf84aae5 added grive version and build time in log file 2012-06-09 14:43:04 +08:00
Nestal Wan e259478fb3 removed redundant function 2012-06-09 14:23:39 +08:00
Nestal Wan b8a1d02ec8 updated RPM spec from Vascom 2012-06-09 11:03:46 +08:00
Nestal Wan facdf88058 log uploading of files 2012-06-09 11:02:18 +08:00
Nestal Wan 271dd95b24 escape the document header. fix HTTP 400 when uploading 2012-06-07 18:26:57 +08:00
Nestal Wan 3d27b1af69 force using boost v1.44 or later (#51) 2012-06-07 17:56:03 +08:00
Nestal Wan d24440fa48 added static library (by Jose) 2012-06-07 00:14:21 +08:00
Nestal Wan 01effa1f60 revised the log messages 2012-06-06 23:40:17 +08:00
Nestal Wan c81bb0aaec fixed assertion if remote has dot file (#43) 2012-06-06 23:35:29 +08:00
Nestal Wan 856914c4dc added catch exception for auth code 2012-06-06 23:21:04 +08:00
Nestal Wan b6aafd7ccd fix MD5 and remote XML dump 2012-06-05 23:29:37 +08:00
Nestal Wan 8ff04683a8 removing debug code and minor fixes 2012-06-05 23:08:03 +08:00
Nestal Wan cbf40e9b90 fixed uninitialized change stamp 2012-06-05 22:52:14 +08:00
Nestal Wan 935ac608ff handling change feed. some good news about trashed documents 2012-06-05 22:52:14 +08:00
Nestal Wan a09ba40dbc use libgcrypt instead of OpenSSL 2012-06-05 22:51:54 +08:00
Nestal Wan 5cba612084 adding the Feed class 2012-06-04 01:07:14 +08:00
Nestal Wan 8466095e7b adding change stamp 2012-06-03 23:58:28 +08:00
Nestal Wan 28b32c6f43 recursive delete handling for remote_deleted 2012-06-03 21:53:51 +08:00
Nestal Wan 197abfdea6 removed debug verbose setting in curl 2012-06-03 18:34:03 +08:00
Nestal Wan 379dddc532 fully configurable log 2012-06-03 18:31:02 +08:00
Nestal Wan 53f9c39941 disable debug log during authenication 2012-06-03 17:31:16 +08:00
Nestal Wan 54b65bb738 update the resource by a GET before deleting it 2012-06-03 16:57:21 +08:00
Nestal Wan c17ba4e880 added the http::Header class. should add it long ago. 2012-06-03 16:42:51 +08:00
Nestal Wan 81f71f8406 fixed missing assignment of entry IDs 2012-06-03 15:55:32 +08:00
Nestal Wan 3d7ad69915 don't delete the file if its parent folder will be deleted 2012-06-03 14:44:09 +08:00
Nestal Wan 5edd71b69e Merge branch 'manuken' 2012-06-02 15:50:15 +08:00
Nestal Wan ef8b43ae59 added rpm spec file from manuken 2012-06-02 15:46:28 +08:00
Nestal Wan 15c582159a don't log debug info by default 2012-06-02 10:31:37 +08:00
Nestal Wan 88cdb576c4 use ctime instead of mtime to detect changes 2012-06-01 23:30:04 +08:00
Matchman Green b6fb4a604e preparing for v0.1.0 release 2012-06-01 00:50:56 +08:00
Matchman Green da6c99089f enabled code to "delete" files 2012-06-01 00:40:07 +08:00
Matchman Green 59d0761d68 minor refactoring 2012-05-31 23:48:30 +08:00
Matchman Green 6db0b4a7aa refactored the creation of resources 2012-05-31 23:37:47 +08:00
Matchman Green 7830136518 fixed the log messages and some state update 2012-05-31 19:22:18 +08:00
Matchman Green 377d8186b1 trying to give more hint about libiberty 2012-05-31 18:38:48 +08:00
Matchman Green 7e5b7e0b7d should be able to detect new and deleted files correctly now 2012-05-31 01:17:22 +08:00
Matchman Green 8b1b388713 not tracking the status of all files/dir in .grive_state 2012-05-30 01:30:06 +08:00
Matchman Green 167e2eaac5 fixed create subfolder & seg fault 2012-05-29 00:31:29 +08:00
manuken 645fa5a01c Changes in the spec file 2012-05-28 15:29:28 +02:00
manuken b0dd9a53db Merge branch 'master' of https://github.com/match065/grive 2012-05-28 02:13:42 +01:00
Matchman Green 07553e51fa ignore file and folders with '/' 2012-05-28 00:36:02 +08:00
Matchman Green bb6124c02c can now create new folders 2012-05-28 00:24:37 +08:00
Matchman Green 729ec106f8 no need to check MD5 or mtime for folders 2012-05-27 22:49:49 +08:00
Matchman Green e09bdc5a8a trying to add fdopen 2012-05-27 14:55:26 +08:00
manuken d43d3c5c1d Revert "Revert "Ignore DS_STORE""
This reverts commit 82a308fd8a.
2012-05-26 22:23:43 +01:00
manuken 82a308fd8a Revert "Ignore DS_STORE"
This reverts commit fc6c87cc30.
2012-05-26 22:23:41 +01:00
manuken fc6c87cc30 Ignore DS_STORE
Mac OSX
2012-05-26 22:23:10 +01:00
manuken 2b69c9f2e0 Created a Fedora 16 RPM 2012-05-26 13:41:33 +01:00
Matchman Green d78728938f added resource test 2012-05-25 01:24:59 +08:00
Matchman Green 61c01019f7 added more test cases and some minor refactoring 2012-05-25 01:24:59 +08:00
Matchman Green 050c89c3d8 trying to make libiberty optional 2012-05-25 00:56:19 +08:00
Matchman Green 97884cf25c improved HTTP error handling to troubleshoot issue #31 2012-05-24 23:50:23 +08:00
Matchman Green ade83c6d70 added libiberty. now can be built in OpenSUSE 12.1.
Thanks Arif Widi Nugroho for the help!
2012-05-24 23:25:49 +08:00
Matchman Green 50f1d813e2 set the mode of .grive to 0600 for security.
Thanks Huin for the reminder.
2012-05-24 19:06:03 +08:00
Matchman Green a0570d507c Revert "Add OS notification support (libnotify)"
This reverts commit 428afa6538.
2012-05-24 18:57:37 +08:00
Matchman Green a49794897a added Chmod for files 2012-05-24 18:56:48 +08:00
daniele 428afa6538 Add OS notification support (libnotify) 2012-05-24 18:10:30 +08:00
Matchman Green 2fc4ffd1bf Merge branch 'gontadu' 2012-05-24 17:58:36 +08:00
Matchman Green 3c769a720a remove Gdbm stuff as it won't compile on some system 2012-05-24 17:52:04 +08:00
Eshwar Andhavarapu 53231d928d Corrected grammar slightly :) 2012-05-24 01:50:57 +03:00
Matchman Green c01ace7d18 can upload new files to remote 2012-05-21 01:18:16 +08:00
Matchman Green c7c49b094a fixed upload 2012-05-21 00:10:29 +08:00
Matchman Green 99f6e4cc58 improved logging and fixed exception on MD5 calculation 2012-05-20 23:16:59 +08:00
Matchman Green 90bf78e716 minor clean up 2012-05-20 19:28:44 +08:00
Matchman Green dfbe859531 load and save from .grive_state and build the resource tree 2012-05-20 17:12:01 +08:00
Matchman Green 3dd3f544fb minor clean up 2012-05-20 15:25:38 +08:00
Matchman Green 6453de2cf6 start using state for synchronization 2012-05-20 15:01:53 +08:00
Matchman Green b1589bb181 clean up sync code 2012-05-20 15:01:53 +08:00
Matchman Green 7f2fa95d9a improved checking on files 2012-05-20 15:01:53 +08:00
Matchman Green 13aa9a455b merge the list of the files and folders in the same list 2012-05-20 15:01:53 +08:00
Matchman Green b46c9debf8 minor refactoring 2012-05-20 15:01:53 +08:00
Matchman Green aa03b212f7 removing the folder list in Drive, using state instead 2012-05-20 15:01:53 +08:00
Matchman Green 7882198293 merged the File class to Resource 2012-05-20 15:01:53 +08:00
Matchman Green 50e3091b2a renamed Collection to Resource 2012-05-20 15:01:52 +08:00
Matchman Green 149c1c14b0 replace with FolderSet 2012-05-20 15:01:52 +08:00
Matchman Green 37f2066727 added folder set 2012-05-20 15:01:52 +08:00
Matchman Green 9511492c65 changed version to 0.1.0 2012-05-20 15:01:52 +08:00
Matchman Green cf77a2d5c4 added folder init code in state 2012-05-20 15:01:52 +08:00
Matchman Green 3a33db68b6 replaced the Path class with fs::path 2012-05-20 15:01:52 +08:00
Matchman Green 57fb6d3b0e added state to drive 2012-05-20 15:01:52 +08:00
Matchman Green 6dd505f8a6 added state 2012-05-20 15:01:52 +08:00
Matchman Green 2095d0f956 trying to work around a compiler problem in gcc 4.4.4 2012-05-20 15:00:44 +08:00
Matchman Green 6e3b84d85d properly handle exceptions 2012-05-19 09:03:34 +08:00
Matchman Green f2a659ce5e don't throw even if can't read config 2012-05-17 20:55:51 +08:00
Nestal Wan c326a41d3a Merge pull request #21 from bashlnx/patch-1
Fix build (maybe due to GCC 4.7?)
2012-05-16 05:06:35 -07:00
Andrea Scarpino 1784277c7f Fix build (maybe due to GCC 4.7?)
/builds/grive-git/src/grive/libgrive/src/http/Agent.cc: In function ‘void {anonymous}::CallbackInt(int)’:                                      
/builds/grive-git/src/grive/libgrive/src/http/Agent.cc:63:2: error: ‘cout’ is not a member of ‘std’
2012-05-16 14:50:07 +03:00
Matchman Green 53883ab8bd move the .grive from home directory to current directory. get change stamp 2012-05-16 00:54:58 +08:00
Matchman Green 0b63d9bd3c added config file class, but not very well encapsulated 2012-05-15 23:07:03 +08:00
Matchman Green 3dea8343de added code to save and load change_stamp 2012-05-14 23:33:29 +08:00
Matchman Green 30f9834968 saved the metadata of the files 2012-05-14 23:33:29 +08:00
Matchman Green 3c8c6bab54 Revert "adding xattr". seems it's better not to use xattr
This reverts commit daf5bec3a1ec4c18b3326376ab6260bd1019b0cb.
2012-05-14 23:33:29 +08:00
Matchman Green 31800ae2f1 adding xattr 2012-05-14 23:33:28 +08:00
Matchman Green 35c2ef9fc1 use boost exception macro to throw exception 2012-05-14 23:32:22 +08:00
Matchman Green 881ff16d05 preparing for the v0.0.5 release 2012-05-13 17:30:07 +08:00
Matchman Green 1f551ee321 added file log. fixed bug on getting upload link 2012-05-13 17:27:40 +08:00
Matchman Green 4408f51300 improved and simplified log API 2012-05-13 16:45:27 +08:00
Matchman Green 63553aae04 added log interface 2012-05-13 16:10:18 +08:00
Matchman Green 8ced12d5a1 used new exception classes 2012-05-13 15:27:58 +08:00
Matchman Green 66e7783a9c added backtrace 2012-05-13 14:08:04 +08:00
Matchman Green 9631031931 ignore files and folders that is not inside the root tree.
also ignore files/folders with multiple parents
2012-05-13 01:57:15 +08:00
Matchman Green 931f743b58 use showroot=true. ignoring folders not in root folder 2012-05-12 18:48:55 +08:00
Matchman Green 4da9d0bc00 removed old Json code 2012-05-12 18:24:42 +08:00
Matchman Green b2d0bf2ba8 use XML for downloading files and folders 2012-05-12 17:38:41 +08:00
Matchman Green 444bf61c73 added copy ctor & op= to NodeSet 2012-05-12 12:27:35 +08:00
Matchman Green c1c1916eb7 improved Find() in SetSet 2012-05-12 11:47:51 +08:00
Matchman Green ceffebb097 changed operator[] to return NodeSet 2012-05-12 11:09:31 +08:00
Matchman Green 05cae069c6 added NodeSet 2012-05-12 10:49:22 +08:00
Matchman Green f80a5f0fb7 trying to read properties from entry XML 2012-05-12 10:49:22 +08:00
Matchman Green 826db0e064 added UT helper 2012-05-12 10:49:22 +08:00
Matchman Green 204e0db62e used boost::iterator_adaptor 2012-05-12 10:49:22 +08:00
Matchman Green 155f4dae77 Merge pull request #18 from Sunnywout/patch-1
Edited messages, made them correct English and fixed spelling
2012-05-11 18:22:08 -07:00
Sunnywout 47abf86273 Edited messages, made them correct English and fixed spelling 2012-05-11 21:20:32 +03:00
Matchman Green 0c3fdaa921 trying to use LIB_INSTALL_DIR 2012-05-10 22:30:11 +08:00
Matchman Green d3b841ccfd added debug logs. the XML nodes need to be improved 2012-05-10 01:20:38 +08:00
Matchman Green 1ae7a18f63 Merge branch 'working' 2012-05-10 00:31:02 +08:00
Matchman Green 93f0f42855 missing '$' 2012-05-10 00:30:30 +08:00
Matchman Green 55fb39cbc1 adding log to http agent 2012-05-10 00:30:01 +08:00
Matchman Green efaa5df229 renamed HTTP.(hh|cc) to Agent 2012-05-10 00:22:27 +08:00
Matchman Green b51e5c593f reuse the Entry class in Collection 2012-05-09 23:52:06 +08:00
Matchman Green 56e605c392 renamed File to Entry, because it is common between file & folder 2012-05-09 23:09:19 +08:00
Matchman Green 7a66605a5f Merge branch 'v0.0.4' 2012-05-09 22:31:48 +08:00
Matchman Green b8153d05f9 Merge branch 'master' of matchgit:match065/grive
Conflicts:
	libgrive/src/drive/Drive.cc
2012-05-09 22:31:37 +08:00
Matchman Green 5e8c6a1cee changed version to 0.0.4 2012-05-09 22:29:37 +08:00
Matchman Green 175fe855fe added lib32 & lib64 library install path destination 2012-05-09 22:26:38 +08:00
Matchman Green 06158e6bf0 fixed state reuse in the receivables 2012-05-09 22:25:42 +08:00
Matchman Green b0f5769cb2 fixed state reuse in the receivables 2012-05-09 00:58:05 +08:00
Matchman Green d621aa4296 improved the node printing function 2012-05-07 00:03:33 +08:00
Matchman Green 6ad573942a fixed crash. missing AddRef() 2012-05-06 23:30:14 +08:00
Matchman Green c00ffef6a3 Merge branch 'master' into working 2012-05-06 23:17:24 +08:00
Matchman Green 70669fe290 Merge remote-tracking branch 'origin/master' 2012-05-06 23:16:51 +08:00
Matchman Green 65ae81bb96 node iterator still being worked 2012-05-06 23:16:43 +08:00
Matchman Green 9b574b5669 Merge pull request #13 from OpenNingia/master
Patch CMakeLists to check for LIBEXPAT
2012-05-06 08:12:43 -07:00
Matchman Green 3584e12152 Merge branch 'v0.0.4' into working 2012-05-06 22:54:52 +08:00
Matchman Green 98f1bba4ab added -v to display version 2012-05-06 22:40:58 +08:00
Matchman Green 1098e7e335 added code to remove files 2012-05-06 22:27:52 +08:00
Daniele Simonetti d891d1fefd add LIBEXPAT requirements to CMakeLists 2012-05-06 16:25:37 +02:00
Matchman Green 7363c7a49a Merge branch 'v0.0.4' into working
Conflicts:
	libgrive/src/drive/Drive.cc
2012-05-06 18:26:45 +08:00
Matchman Green 1eeaede474 fetching the next link 2012-05-06 18:14:36 +08:00
Matchman Green 1aaca87ad0 implementing iterator for XML nodes 2012-05-06 17:13:48 +08:00
Matchman Green f987888cac character handling in XML 2012-05-06 11:50:37 +08:00
Matchman Green 5e615de7cd added class to parse XML in HTTP response 2012-05-06 02:37:54 +08:00
Matchman Green b42e7c6501 move http files to a separate directory 2012-05-06 02:21:39 +08:00
Matchman Green 1c7b114ea5 refactored to use only the new http interface 2012-05-06 02:15:28 +08:00
Matchman Green 1e6e6a4347 added HTTP receiver callback interface 2012-05-06 01:12:44 +08:00
Matchman Green ba737cba29 Merge branch 'v0.0.4' into working 2012-05-05 23:19:59 +08:00
Matchman Green b90e032cda properly handle read-only files 2012-05-05 22:54:36 +08:00
Matchman Green 8357e9ed82 renamed HTTP agent class and namespace 2012-05-05 22:39:18 +08:00
Matchman Green b89f7bb873 refactored to allow progressive parsing 2012-05-05 15:25:21 +08:00
Matchman Green 19c329836b separated name & value 2012-05-05 15:00:23 +08:00
Matchman Green b12edc2a2a added expat parser 2012-05-05 13:01:23 +08:00
Matchman Green 7d2b8b441d adding xml tree basics 2012-05-04 01:44:52 +08:00
Matchman Green 7b8d22b2b7 Merge remote-tracking branch 'origin/master' into v0.0.3 2012-05-03 22:54:25 +08:00
Matchman Green 51cd1ed40e Merge pull request #10 from massix/master
Some headers were missing in the installation process
2012-05-03 06:59:12 -07:00
Massimo Gengarelli e563f7cad6 Remove old and unwanted CMakeLists 2012-05-02 22:58:55 +02:00
Massimo Gengarelli 2eccd9df2c Install all the headers in CMAKE_PREFIX/include 2012-05-02 22:10:14 +02:00
163 changed files with 11922 additions and 2066 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
.git
Dockerfile
.dockerignore
.gitignore

23
.gitignore vendored
View File

@ -1,6 +1,27 @@
.kdev4
grive.kdev4
.DS_Store
.project
.cproject
build/
/CMakeCache.txt
CMakeFiles
moc_*.cxx*
bgrive/ui_MainWindow.h
Makefile
*.a
bgrive/bgrive
grive/grive
libgrive/btest
*.cmake
debian/debhelper-build-stamp
debian/files
debian/grive.debhelper.log
debian/grive.substvars
debian/grive/
debian/.debhelper
obj-x86_64-linux-gnu/
.idea

View File

@ -1,4 +1,27 @@
cmake_minimum_required(VERSION 2.8)
project(grive2)
include(GNUInstallDirs)
# Grive version. remember to update it for every new release!
set( GRIVE_VERSION "0.5.3" CACHE STRING "Grive version" )
message(WARNING "Version to build: ${GRIVE_VERSION}")
# common compile options
add_definitions( -DVERSION="${GRIVE_VERSION}" )
add_definitions( -D_FILE_OFFSET_BITS=64 -std=c++0x )
if ( APPLE )
add_definitions( -Doff64_t=off_t )
endif ( APPLE )
find_program(
HAVE_SYSTEMD systemd
PATHS /lib/systemd /usr/lib/systemd
NO_DEFAULT_PATH
)
if ( HAVE_SYSTEMD )
add_subdirectory( systemd )
endif( HAVE_SYSTEMD )
add_subdirectory( libgrive )
add_subdirectory( grive )

27
Dockerfile Normal file
View File

@ -0,0 +1,27 @@
FROM alpine:3.7 as build
RUN apk add git make cmake g++ libgcrypt-dev yajl-dev yajl \
boost-dev curl-dev expat-dev cppunit-dev binutils-dev \
pkgconfig \
&& git clone https://github.com/vitalif/grive2.git \
&& mkdir grive2/build \
&& cd grive2/build \
&& cmake .. \
&& make -j4 \
&& make install \
&& cd ../.. \
&& rm -rf grive2 \
&& mkdir /drive
FROM alpine:3.7
COPY --from=build /usr/local/bin/grive /bin/grive
ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64 /bin/dumb-init
RUN chmod 777 /bin/dumb-init /bin/grive \
&& mkdir /data \
&& apk add yajl-dev curl-dev libgcrypt \
boost-program_options boost-regex libstdc++ boost-system boost-dev binutils-dev \
&& apk add boost-filesystem --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main
VOLUME /data
WORKDIR /data
ENTRYPOINT ["dumb-init", "grive"]

27
README
View File

@ -1,27 +0,0 @@
Grive 0.0.3
Grive is still considered experimental. It just download all the files in your google drive
in the current directory. After you make some changes to the local files, run grive and
it will upload your changes back to your google drive.
There are a few things that grive does not do at the moment:
- wait for changes in file system to occur and upload. Grive only sync when you run it.
- create new files in google drive. Grive only uploads changed files that already
exist in server.
Of course there will be done in future, possibly the next release.
This is compiled in Fedora 16 x64. You need the following libraries to run:
json-c, libcurl, libstdc++ and openssl. CppUnit is also optional and required
only if you want to build the test cases.
For the first time running grive, you should use the "-a" argument to grant
permission to grive to access to your Google Drive. An URL should be printed.
Go to the page. You will need to login to your google account if you haven't
done so. After granting the permission to grive, the browser will show you
an authenication code. Copy-and-paste that to the standard input of grive.
If everything works fine, grive will create a .grive file in your home directory.
It will also start downloading files from your Google Drive to your current directory.
Enjoy!

270
README.md Normal file
View File

@ -0,0 +1,270 @@
# Grive2 0.5.3
09 Nov 2022, Vitaliy Filippov
http://yourcmc.ru/wiki/Grive2
This is the fork of original "Grive" (https://github.com/Grive/grive) Google Drive client
with the support for the new Drive REST API and partial sync.
Grive simply downloads all the files in your Google Drive into the current directory.
After you make some changes to the local files, run
grive again and it will upload your changes back to your Google Drive. New files created locally
or in Google Drive will be uploaded or downloaded respectively. Deleted files will also be "removed".
Currently Grive will NOT destroy any of your files: it will only move the files to a
directory named .trash or put them in the Google Drive trash. You can always recover them.
There are a few things that Grive does not do at the moment:
- continously wait for changes in file system or in Google Drive to occur and upload.
A sync is only performed when you run Grive (there are workarounds for almost
continuous sync. See below).
- symbolic links support.
- support for Google documents.
These may be added in the future.
Enjoy!
## Usage
When Grive is run for the first time, you should use the "-a" argument to grant
permission to Grive to access to your Google Drive:
```bash
cd $HOME
mkdir google-drive
cd google-drive
grive -a
```
A URL should be printed. Go to the link. You will need to login to your Google
account if you haven't done so. After granting the permission to Grive, the
authorization code will be forwarded to the Grive application and you will be
redirected to a localhost web page confirming the authorization.
If everything works fine, Grive will create .grive and .grive\_state files in your
current directory. It will also start downloading files from your Google Drive to
your current directory.
To resync the direcory, run `grive` in the folder.
```bash
cd $HOME/google-drive
grive
```
### Exclude specific files and folders from sync: .griveignore
Rules are similar to Git's .gitignore, but may differ slightly due to the different
implementation.
- lines that start with # are comments
- leading and trailing spaces ignored unless escaped with \
- non-empty lines without ! in front are treated as "exclude" patterns
- non-empty lines with ! in front are treated as "include" patterns
and have a priority over all "exclude" ones
- patterns are matched against the filenames relative to the grive root
- a/**/b matches any number of subpaths between a and b, including 0
- **/a matches `a` inside any directory
- b/** matches everything inside `b`, but not b itself
- \* matches any number of any characters except /
- ? matches any character except /
- .griveignore itself isn't ignored by default, but you can include it in itself to ignore
### Scheduled syncs and syncs on file change events
There are tools which you can use to enable both scheduled syncs and syncs
when a file changes. Together these gives you an experience almost like the
Google Drive clients on other platforms (it misses the almost instantious
download of changed files in the google drive).
Grive installs such a basic solution which uses inotify-tools together with
systemd timer and services. You can enable it for a folder in your `$HOME`
directory (in this case the `$HOME/google-drive`):
First install the `inotify-tools` (seems to be named like that in all major distros):
test that it works by calling `inotifywait -h`.
Prepare a Google Drive folder in your $HOME directory with `grive -a`.
```bash
# 'google-drive' is the name of your Google Drive folder in your $HOME directory
systemctl --user enable grive@$(systemd-escape google-drive).service
systemctl --user start grive@$(systemd-escape google-drive).service
```
You can enable and start this unit for multiple folders in your `$HOME`
directory if you need to sync with multiple google accounts.
You can also only enable the time based syncing or the changes based syncing
by only directly enabling and starting the corresponding unit:
`grive-changes@$(systemd-escape google-drive).service` or
`grive-timer@$(systemd-escape google-drive).timer`.
### Shared files
Files and folders which are shared with you don't automatically show up in
your folder. They need to be added explicitly to your Google Drive: go to the
Google Drive website, right click on the file or folder and chose 'Add to My
Drive'.
### Different OAuth2 client to workaround over quota and google approval issues
Google recently started to restrict access for unapproved applications:
https://developers.google.com/drive/api/v3/about-auth?hl=ru
Grive2 is currently awaiting approval but it seems it will take forever.
Also even if they approve it the default Client ID supplied with grive may
exceed quota and grive will then fail to sync.
You can supply your own OAuth2 client credentials to work around these problems
by following these steps:
1. Go to https://console.developers.google.com/apis/api/drive.googleapis.com
2. Choose a project (you might need to create one first)
3. Go to https://console.developers.google.com/apis/library/drive.googleapis.com and
"Enable" the Google Drive APIs
4. Go to https://console.cloud.google.com/apis/credentials and click "Create credentials > Help me choose"
5. In the "Find out what credentials you need" dialog, choose:
- Which API are you using: "Google Drive API"
- Where will you be calling the API from: "Other UI (...CLI...)"
- What data will you be accessing: "User Data"
6. In the next steps create a client id (name doesn't matter) and
setup the consent screen (defaults are ok, no need for any URLs)
7. The needed "Client ID" and "Client Secret" are either in the shown download
or can later found by clicking on the created credential on
https://console.developers.google.com/apis/credentials/
8. When you change client ID/secret in an existing Grive folder you must first delete
the old `.grive` configuration file.
9. Call `grive -a --id <client_id> --secret <client_secret>` and follow the steps
to authenticate the OAuth2 client to allow it to access your drive folder.
## Installation
For the detailed instructions, see http://yourcmc.ru/wiki/Grive2#Installation
### Install dependencies
You need the following libraries:
- yajl 2.x
- libcurl
- libstdc++
- libgcrypt
- Boost (Boost filesystem, program_options, regex, unit_test_framework and system are required)
- expat
There are also some optional dependencies:
- CppUnit (for unit tests)
- libbfd (for backtrace)
- binutils (for libiberty, required for compilation in OpenSUSE, Ubuntu, Arch and etc)
On a Debian/Ubuntu/Linux Mint machine just run the following command to install all
these packages:
sudo apt-get install git cmake build-essential libgcrypt20-dev libyajl-dev \
libboost-all-dev libcurl4-openssl-dev libexpat1-dev libcppunit-dev binutils-dev \
debhelper zlib1g-dev dpkg-dev pkg-config
Fedora:
sudo dnf install git cmake libgcrypt-devel gcc-c++ libstdc++ yajl-devel boost-devel libcurl-devel expat-devel binutils zlib
FreeBSD:
pkg install git cmake boost-libs yajl libgcrypt pkgconf cppunit libbfd
### Build Debian packages
On a Debian/Ubuntu/Linux Mint you can use `dpkg-buildpackage` utility from `dpkg-dev` package
to build grive. Just clone the repository, `cd` into it and run
dpkg-buildpackage -j4 --no-sign
### Manual build
Grive uses cmake to build. Basic install sequence is
mkdir build
cd build
cmake ..
make -j4
sudo make install
Alternativly you can define your own client_id and client_secret during build
mkdir build
cd build
cmake .. "-DAPP_ID:STRING=<client_id>" "-DAPP_SECRET:STRING=<client_secret>"
make -j4
sudo make install
## Version History
### Grive2 v0.5.3
- Implement Google OAuth loopback IP redirect flow
- Various small fixes
### Grive2 v0.5.1
- Support for .griveignore
- Automatic sync solution based on inotify-tools and systemd
- no-remote-new and upload-only modes
- Ignore regexp does not persist anymore (note that Grive will still track it to not
accidentally delete remote files when changing ignore regexp)
- Added options to limit upload and download speed
- Faster upload of new and changed files. Now Grive uploads files without first calculating
md5 checksum when file is created locally or when its size changes.
- Added -P/--progress-bar option to print ASCII progress bar for each processed file (pull request by @svartkanin)
- Added command-line options to specify your own client_id and client_secret
- Now grive2 skips links, sockets, fifos and other unusual files
- Various small build fixes
### Grive2 v0.5
- Much faster and more correct synchronisation using local modification time and checksum cache (similar to git index)
- Automatic move/rename detection, -m option removed
- force option works again
- Instead of crashing on sync exceptions Grive will give a warning and attempt to sync failed files again during the next run.
- Revision support works again. Grive 0.4.x always created new revisions for all files during sync, regardless of the absence of the --new-rev option.
- Shared files now sync correctly
### Grive2 v0.4.2
- Option to exclude files by perl regexp
- Reimplemented HTTP response logging for debug purposes
- Use multipart uploads (update metadata and contents at the same time) for improved perfomance & stability
- Bug fixes
- Simple option to move/rename files and directories, via `grive -m oldpath newpath` (by Dylan Wulf, wulfd1@tcnj.edu)
Known issues:
- force option does not work as documented #51
### Grive2 v0.4.1
- Bug fixes
### Grive2 v0.4.0
First fork release, by Vitaliy Filippov / vitalif at mail*ru
- Support for the new Google Drive REST API (old "Document List" API is shut down by Google 20 April 2015)
- REAL support for partial sync: syncs only one subdirectory with `grive -s subdir`
- Major refactoring - a lot of dead code removed, JSON-C is not used anymore, API-specific code is split from non-API-specific
- Some stability fixes from Visa Putkinen https://github.com/visap/grive/commits/visa
- Slightly reduce number of syscalls when reading local files.
### Grive v0.3
Bug fix & minor feature release. Fixed bugs:
- #93: missing reference count increment in one of the Json constructors
- #82: retry for HTTP error 500 & 503
- #77: Fixed a bug where grive crashed on the first run.
New features:
- #87: support for revisions
- #86: ~~partial sync (contributed by justin at tierramedia.com)~~ that's not partial sync,
that's only support for specifying local path on command line

View File

@ -0,0 +1,12 @@
find_library( DL_LIBRARY NAMES dl PATH ${CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES} )
find_library( BFD_LIBRARY NAMES bfd PATH ${CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES} )
if ( DL_LIBRARY AND BFD_LIBRARY )
set( BFD_FOUND TRUE )
endif (DL_LIBRARY AND BFD_LIBRARY)
if ( BFD_FOUND )
message( STATUS "Found libbfd: ${BFD_LIBRARY}")
endif ( BFD_FOUND )

View File

@ -0,0 +1,22 @@
# - Find Iberty
# This module finds libiberty.
#
# It sets the following variables:
# IBERTY_LIBRARY - The library to link against.
FIND_LIBRARY( IBERTY_LIBRARY NAMES iberty )
IF (IBERTY_LIBRARY)
# show which library was found only if not quiet
MESSAGE( STATUS "Found libiberty: ${IBERTY_LIBRARY}")
SET(IBERTY_FOUND TRUE)
ELSE (IBERTY_LIBRARY)
IF ( IBERTY_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find libiberty. try to install binutil-devel?")
ENDIF (IBERTY_FIND_REQUIRED)
ENDIF (IBERTY_LIBRARY)

View File

@ -1,30 +0,0 @@
# - Find JSON-C
# This module finds an installed JSON-C package.
#
# It sets the following variables:
# JSONC_FOUND - Set to false, or undefined, if JSON-C isn't found.
# JSONC_INCLUDE_DIR - The JSON-C include directory.
# JSONC_LIBRARY - The JSON-C library to link against.
FIND_PATH(JSONC_INCLUDE_DIR json/json.h)
FIND_LIBRARY(JSONC_LIBRARY NAMES json)
IF (JSONC_INCLUDE_DIR AND JSONC_LIBRARY)
SET(JSONC_FOUND TRUE)
ENDIF (JSONC_INCLUDE_DIR AND JSONC_LIBRARY)
IF (JSONC_FOUND)
# show which JSON-C was found only if not quiet
IF (NOT JSONC_FIND_QUIETLY)
MESSAGE(STATUS "Found JSON-C: ${JSONC_LIBRARY}")
ENDIF (NOT JSONC_FIND_QUIETLY)
ELSE (JSONC_FOUND)
# fatal error if JSON-C is required but not found
IF (JSONC_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find JSON-C")
ENDIF (JSONC_FIND_REQUIRED)
ENDIF (JSONC_FOUND)

View File

@ -0,0 +1,50 @@
# - Try to find the Gcrypt library
# Once run this will define
#
# LIBGCRYPT_FOUND - set if the system has the gcrypt library
# LIBGCRYPT_CFLAGS - the required gcrypt compilation flags
# LIBGCRYPT_LIBRARIES - the linker libraries needed to use the gcrypt library
#
# Copyright (c) 2006 Brad Hards <bradh@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
# libgcrypt is moving to pkg-config, but earlier version don't have it
#search in typical paths for libgcrypt-config
FIND_PROGRAM(LIBGCRYPTCONFIG_EXECUTABLE NAMES libgcrypt-config)
#reset variables
set(LIBGCRYPT_LIBRARIES)
set(LIBGCRYPT_CFLAGS)
# if libgcrypt-config has been found
IF(LIBGCRYPTCONFIG_EXECUTABLE)
EXEC_PROGRAM(${LIBGCRYPTCONFIG_EXECUTABLE} ARGS --libs RETURN_VALUE _return_VALUE OUTPUT_VARIABLE LIBGCRYPT_LIBRARIES)
EXEC_PROGRAM(${LIBGCRYPTCONFIG_EXECUTABLE} ARGS --cflags RETURN_VALUE _return_VALUE OUTPUT_VARIABLE LIBGCRYPT_CFLAGS)
IF(${LIBGCRYPT_CFLAGS} MATCHES "\n")
SET(LIBGCRYPT_CFLAGS " ")
ENDIF(${LIBGCRYPT_CFLAGS} MATCHES "\n")
IF(LIBGCRYPT_LIBRARIES AND LIBGCRYPT_CFLAGS)
SET(LIBGCRYPT_FOUND TRUE)
ENDIF(LIBGCRYPT_LIBRARIES AND LIBGCRYPT_CFLAGS)
ENDIF(LIBGCRYPTCONFIG_EXECUTABLE)
if (LIBGCRYPT_FOUND)
if (NOT LibGcrypt_FIND_QUIETLY)
message(STATUS "Found libgcrypt: ${LIBGCRYPT_LIBRARIES}")
endif (NOT LibGcrypt_FIND_QUIETLY)
else (LIBGCRYPT_FOUND)
if (LibGcrypt_FIND_REQUIRED)
message(FATAL_ERROR "Could not find libgcrypt libraries")
endif (LibGcrypt_FIND_REQUIRED)
endif (LIBGCRYPT_FOUND)
MARK_AS_ADVANCED(LIBGCRYPT_CFLAGS LIBGCRYPT_LIBRARIES)

63
completion.zsh Normal file
View File

@ -0,0 +1,63 @@
#compdef grive
# ------------------------------------------------------------------------------
# Copyright (c) 2015 Github zsh-users - http://github.com/zsh-users
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the zsh-users nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL ZSH-USERS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ------------------------------------------------------------------------------
# Description
# -----------
#
# Completion script for Grive (https://github.com/vitalif/grive2)
#
# ------------------------------------------------------------------------------
# Authors
# -------
#
# * Doron Behar <https://github.com/doronbehar>
#
# ------------------------------------------------------------------------------
# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
# vim: ft=zsh sw=2 ts=2 et
# ------------------------------------------------------------------------------
local curcontext="$curcontext" state line ret=1
typeset -A opt_args
_arguments -C \
'(-h --help)'{-h,--help}'[Produce help message]' \
'(-v --version)'{-v,--version}'[Display Grive version]' \
'(-a --auth)'{-a,--auth}'[Request authorization token]' \
'(-p --path)'{-p,--path}'[Root directory to sync]' \
'(-s --dir)'{-s,--dir}'[Single subdirectory to sync (remembered for next runs)]' \
'(-V --verbose)'{-V,--verbose}'[Verbose mode. Enable more messages than normal.]' \
'(--log-http)--log-http[Log all HTTP responses in this file for debugging.]' \
'(--new-rev)--new-rev[Create,new revisions in server for updated files.]' \
'(-d --debug)'{-d,--debug}'[Enable debug level messages. Implies -v.]' \
'(-l --log)'{-l,--log}'[Set log output filename.]' \
'(-f --force)'{-f,--force}'[Force grive to always download a file from Google Drive instead of uploading it.]' \
'(--dry-run)--dry-run[Only,detect which files need to be uploaded/downloaded,without actually performing them.]' \
'(--ignore)--ignore[Perl,RegExp to ignore files (matched against relative paths, remembered for next runs) ]' \
'*: :_files' && ret=0
return ret

99
debian/changelog vendored Normal file
View File

@ -0,0 +1,99 @@
grive2 (0.5.3) unstable; urgency=medium
* Implement Google OAuth loopback IP redirect flow
* Various small fixes
-- Vitaliy Filippov <vitalif@yourcmc.ru> Wed, 09 Nov 2022 12:42:28 +0300
grive2 (0.5.2+git20210315) unstable; urgency=medium
* Newer dev version
* Add systemd unit files and helper script for automatic syncs
* Add possibility to change client id and secret and save it between runs
-- Vitaliy Filippov <vitalif@yourcmc.ru> Wed, 31 Jul 2016 22:04:53 +0300
grive2 (0.5+git20160114) unstable; urgency=medium
* Newer release, with support for faster sync and rename detection
-- Vitaliy Filippov <vitalif@yourcmc.ru> Sun, 03 Jan 2016 12:51:55 +0300
grive2 (0.4.1+git20151011) unstable; urgency=medium
* Add Debian packaging scripts to the official repository
-- Vitaliy Filippov <vitalif@yourcmc.ru> Sun, 11 Oct 2015 15:03:55 +0300
grive2 (0.4.1-1+git20151008~webupd8~wily0) wily; urgency=medium
* new git pull
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Thu, 08 Oct 2015 11:50:55 +0200
grive2 (0.4.1-1+git20151007~webupd8~wily0) wily; urgency=medium
* new git pull
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Wed, 07 Oct 2015 13:46:29 +0200
grive2 (0.4.1-1+git20151001~webupd8~precise0) precise; urgency=medium
* new git pull
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Thu, 01 Oct 2015 12:54:44 +0200
grive2 (0.4.0-1+git20150928~webupd8~wily1) wily; urgency=medium
* new git pull
* build-depend on libboost-regex-dev
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Mon, 28 Sep 2015 11:36:19 +0200
grive2 (0.4.0-1+git20150810~webupd8~wily0) wily; urgency=medium
* upload for Wily
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Sat, 04 Jul 2015 12:26:14 +0200
grive2 (0.4.0-1+git20150810~webupd8~vivid0) vivid; urgency=medium
* new git pull
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Sat, 04 Jul 2015 12:26:14 +0200
grive2 (0.4.0-1+git20150629~webupd8~vivid0) vivid; urgency=medium
* new git pull
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Mon, 29 Jun 2015 12:27:21 +0200
grive2 (0.4.0-1~webupd8~precise3) precise; urgency=medium
* Initial packaging (based on grive)
* For Precise and Trusty only: use libgcrypt11 instead of libgcrypt20
because libcurl4-openssl-dev depends on it (and for Precise,
there's no libgcrypt20)
-- Alin Andrei <webupd8@gmail.com> Mon, 25 May 2015 15:31:24 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
11

25
debian/control vendored Normal file
View File

@ -0,0 +1,25 @@
Source: grive2
Section: net
Priority: optional
Maintainer: Vitaliy Filippov <vitalif@mail.ru>
Build-Depends: debhelper, cmake, pkg-config, zlib1g-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libboost-filesystem-dev, libboost-program-options-dev, libboost-test-dev, libboost-regex-dev, libexpat1-dev, libgcrypt-dev, libyajl-dev
Standards-Version: 3.9.6
Homepage: https://yourcmc.ru/wiki/Grive2
Package: grive
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: Grive2: an open source Linux client for Google Drive
.
This is the up-to-date fork of the original "Grive" (https://github.com/Grive/grive)
Google Drive client with the support for the new Drive REST API and partial
sync.
.
For the first time running grive, you should use the "-a" argument to grant
permission to grive to access to your Google Drive. An URL should be printed.
Go to the page. You will need to login to your google account if you haven't
done so. After granting the permission to grive, the browser will show you
an authenication code. Copy-and-paste that to the standard input of grive.
If everything works fine, grive will create .grive and .grive_state inside the
synchronized directory. It will also start downloading files from your Google
Drive to that directory.

20
debian/copyright vendored Normal file
View File

@ -0,0 +1,20 @@
Grive2
https://github.com/vitalif/grive2
Current developers:
Vitaliy Filippov <vitalif@mail.ru>
Previous developers:
Nestal Wan (me@nestal.net) 16.05.2012 — 03.05.2013
Matchman Green (match065@gmail.com) 26.04.2012 — 20.06.2012
Contributors:
See full list here https://yourcmc.ru/wiki/Grive2#Full_list_of_contributors
License:
GPL 2.0+

7
debian/rules vendored Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/make -f
override_dh_auto_configure:
dh_auto_configure -- -DHAVE_SYSTEMD=1
%:
dh $@ --buildsystem=cmake --parallel --builddirectory=build

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -1,20 +1,44 @@
project( grive )
find_package(Boost COMPONENTS program_options REQUIRED)
include_directories(
${grive_SOURCE_DIR}/../libgrive/src
${OPT_INCS}
${Boost_INCLUDE_DIRS}
)
file (GLOB GRIVE_EXE_SRC
${grive_SOURCE_DIR}/src/*.cc
)
add_executable( grive_executable
src/main.cc
${GRIVE_EXE_SRC}
)
target_link_libraries( grive_executable
grive
grive
)
set(DEFAULT_APP_ID "615557989097-i93d4d1ojpen0m0dso18ldr6orjkidgf.apps.googleusercontent.com")
set(DEFAULT_APP_SECRET "xiM8Apu_WuRRdheNelJcNtOD")
set(APP_ID ${DEFAULT_APP_ID} CACHE STRING "Application Id")
set(APP_SECRET ${DEFAULT_APP_SECRET} CACHE STRING "Application Secret")
target_compile_definitions ( grive_executable
PRIVATE
-DAPP_ID="${APP_ID}"
-DAPP_SECRET="${APP_SECRET}"
)
set_target_properties( grive_executable
PROPERTIES OUTPUT_NAME grive
PROPERTIES OUTPUT_NAME grive
)
install(TARGETS grive_executable RUNTIME DESTINATION bin)
if ( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" )
install(FILES doc/grive.1 DESTINATION man/man1 )
else ( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" )
install(FILES doc/grive.1 DESTINATION share/man/man1 )
endif( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" )

127
grive/doc/grive.1 Normal file
View File

@ -0,0 +1,127 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH "GRIVE" 1 "January 3, 2016"
.SH NAME
grive \- Google Drive client for GNU/Linux
.SH SYNOPSIS
.B grive [OPTIONS]
.SH DESCRIPTION
.PP
.I Grive
is a Google Drive (online storage service) client for GNU/Linux
systems.
.PP
It allows the synchronization of all your files on the cloud with a
directory of your choice and the upload of new files to Google Drive.
.PP
The options are as follows:
.TP
\fB\-a\fR, \fB\-\-auth\fR
Requests authorization token from Google
.TP
\fB\-d\fR, \fB\-\-debug\fR
Enable debug level messages. Implies \-V
.TP
\fB\-\-dry-run\fR
Only detect which files need to be uploaded/downloaded, without actually performing changes
.TP
\fB\-f, \-\-force\fR
Forces
.I grive
to always download a file from Google Drive instead uploading it
.TP
\fB\-u, \-\-upload\-only\fR
Forces
.I grive
to not download anything from Google Drive and only upload local changes to server instead
.TP
\fB\-n, \-\-no\-remote\-new\fR
Forces
.I grive
to download only files that are changed in Google Drive and already exist locally
.TP
\fB\-h\fR, \fB\-\-help\fR
Produces help message
.TP
\fB\-\-ignore\fR <perl_regexp>
Ignore files with relative paths matching this Perl Regular Expression.
.TP
\fB\-l\fR <filename>, \fB\-\-log\fR <filename>
Write log output to
.I <filename>
.TP
\fB\-\-log\-http\fR <filename_prefix>
Log all HTTP responses in files named
.I <filename_prefix>YYYY-MM-DD.HHMMSS.txt
for debugging
.TP
\fB\-\-new\-rev\fR
Create new revisions in server for updated files
.TP
\fB\-p\fR <wc_path>, \fB\-\-path\fR <wc_path>
Use
.I <wc_path>
as the working copy root directory
.TP
\fB\-s\fR <subdir>, \fB\-\-dir\fR <subdir>
Sync a single
.I <subdir>
subdirectory. Internally converted to an ignore regexp.
.TP
\fB\-v\fR, \fB\-\-version\fR
Displays program version
.TP
\fB\-P\fR, \fB\-\-progress-bar\fR
Print ASCII progress bar for each downloaded/uploaded file.
.TP
\fB\-V\fR, \fB\-\-verbose\fR
Verbose mode. Enables more messages than usual.
.SH .griveignore
.PP
You may create .griveignore in your Grive root and use it to setup
exclusion/inclusion rules.
.PP
Rules are similar to Git's .gitignore, but may differ slightly due to the different
implementation.
.IP \[bu]
lines that start with # are comments
.IP \[bu]
leading and trailing spaces ignored unless escaped with \\
.IP \[bu]
non-empty lines without ! in front are treated as "exclude" patterns
.IP \[bu]
non-empty lines with ! in front are treated as "include" patterns
and have a priority over all "exclude" ones
.IP \[bu]
patterns are matched against the filenames relative to the grive root
.IP \[bu]
a/**/b matches any number of subpaths between a and b, including 0
.IP \[bu]
**/a matches `a` inside any directory
.IP \[bu]
b/** matches everything inside `b`, but not b itself
.IP \[bu]
* matches any number of any characters except /
.IP \[bu]
? matches any character except /
.IP \[bu]
\[char46]griveignore itself isn't ignored by default, but you can include it in itself to ignore
.SH AUTHORS
.PP
Current maintainer is Vitaliy Filippov.
.PP
Original author was Nestal Wan.
This manpage was written by José Luis Segura Lucas (josel.segura@gmx.es)
.PP
The full list of contributors may be found here
.I http://yourcmc.ru/wiki/Grive2#Full_list_of_contributors
.SH REPORT BUGS
.PP
.I https://github.com/vitalif/grive2/issues
.I https://groups.google.com/forum/?fromgroups#!forum/grive-devel

View File

@ -17,121 +17,267 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "drive/Drive.hh"
#include "util/Config.hh"
#include "util/ProgressBar.hh"
#include "base/Drive.hh"
#include "drive2/Syncer2.hh"
#include "http/CurlAgent.hh"
#include "protocol/AuthAgent.hh"
#include "protocol/OAuth2.hh"
#include "protocol/Json.hh"
#include "json/Val.hh"
#include "bfd/Backtrace.hh"
#include "util/Exception.hh"
#include "util/log/Log.hh"
#include "util/log/CompositeLog.hh"
#include "util/log/DefaultLog.hh"
// boost header
#include <boost/exception/all.hpp>
#include <boost/program_options.hpp>
// initializing libgcrypt, must be done in executable
#include <gcrypt.h>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <iterator>
#include <exception>
#include <stdexcept>
const std::string default_id = APP_ID ;
const std::string default_secret = APP_SECRET ;
const std::string client_id = "22314510474.apps.googleusercontent.com" ;
const std::string client_secret = "bl4ufi89h-9MkFlypcI7R785" ;
using namespace gr ;
namespace po = boost::program_options;
namespace gr
// libgcrypt insist this to be done in application, not library
void InitGCrypt()
{
class ConfigError : public std::runtime_error
{
public :
ConfigError( const std::string& msg ) : runtime_error( msg )
{
}
} ;
if ( !gcry_check_version(GCRYPT_VERSION) )
throw std::runtime_error( "libgcrypt version mismatch" ) ;
const std::string& ConfigFilename()
{
static const char *env_cfg = ::getenv( "GR_CONFIG" ) ;
static const std::string filename =
(env_cfg != 0) ? env_cfg : std::string( ::getenv( "HOME") ) + "/.grive" ;
// disable secure memory
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
return filename ;
}
// tell Libgcrypt that initialization has completed
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
}
Json ReadConfig()
void InitLog( const po::variables_map& vm )
{
std::unique_ptr<log::CompositeLog> comp_log( new log::CompositeLog ) ;
std::unique_ptr<LogBase> def_log( new log::DefaultLog );
LogBase* console_log = comp_log->Add( def_log ) ;
if ( vm.count( "log" ) )
{
std::ifstream ifile( ConfigFilename().c_str() ) ;
if ( !ifile )
return Json() ;
std::unique_ptr<LogBase> file_log( new log::DefaultLog( vm["log"].as<std::string>() ) ) ;
file_log->Enable( log::debug ) ;
file_log->Enable( log::verbose ) ;
file_log->Enable( log::info ) ;
file_log->Enable( log::warning ) ;
file_log->Enable( log::error ) ;
file_log->Enable( log::critical ) ;
try
{
std::string cfg_str(
(std::istreambuf_iterator<char>( ifile )),
(std::istreambuf_iterator<char>()) ) ;
return Json::Parse( cfg_str ) ;
}
catch ( std::runtime_error& e )
{
throw ConfigError( std::string("cannot open config file ") + e.what() ) ;
}
// log grive version to log file
file_log->Log( log::Fmt("grive version " VERSION " " __DATE__ " " __TIME__), log::verbose ) ;
file_log->Log( log::Fmt("current time: %1%") % DateTime::Now(), log::verbose ) ;
comp_log->Add( file_log ) ;
}
void SaveConfig( const Json& config )
if ( vm.count( "verbose" ) )
{
std::ofstream ofile( ConfigFilename().c_str() ) ;
ofile << config ;
console_log->Enable( log::verbose ) ;
}
if ( vm.count( "debug" ) )
{
console_log->Enable( log::verbose ) ;
console_log->Enable( log::debug ) ;
}
LogBase::Inst( comp_log.release() ) ;
}
int Main( int argc, char **argv )
{
InitGCrypt() ;
// construct the program options
po::options_description desc( "Grive options" );
desc.add_options()
( "help,h", "Produce help message" )
( "version,v", "Display Grive version" )
( "auth,a", "Request authorization token" )
( "id,i", po::value<std::string>(), "Authentication ID")
( "secret,e", po::value<std::string>(), "Authentication secret")
( "print-url", "Only print url for request")
( "path,p", po::value<std::string>(), "Path to working copy root")
( "dir,s", po::value<std::string>(), "Single subdirectory to sync")
( "verbose,V", "Verbose mode. Enable more messages than normal.")
( "log-http", po::value<std::string>(), "Log all HTTP responses in this file for debugging.")
( "new-rev", "Create new revisions in server for updated files.")
( "debug,d", "Enable debug level messages. Implies -v.")
( "log,l", po::value<std::string>(), "Set log output filename." )
( "force,f", "Force grive to always download a file from Google Drive "
"instead of uploading it." )
( "upload-only,u", "Do not download anything from Google Drive, only upload local changes" )
( "no-remote-new,n", "Download only files that are changed in Google Drive and already exist locally" )
( "dry-run", "Only detect which files need to be uploaded/downloaded, "
"without actually performing them." )
( "upload-speed,U", po::value<unsigned>(), "Limit upload speed in kbytes per second" )
( "download-speed,D", po::value<unsigned>(), "Limit download speed in kbytes per second" )
( "progress-bar,P", "Enable progress bar for upload/download of files")
;
po::variables_map vm;
try
{
po::store( po::parse_command_line( argc, argv, desc ), vm );
}
catch( po::error &e )
{
std::cerr << "Options are incorrect. Use -h for help\n";
return -1;
}
po::notify( vm );
// simple commands that doesn't require log or config
if ( vm.count("help") )
{
std::cout << desc << std::endl ;
return 0 ;
}
else if ( vm.count( "version" ) )
{
std::cout
<< "grive version " << VERSION << ' ' << __DATE__ << ' ' << __TIME__ << std::endl ;
return 0 ;
}
// initialize logging
InitLog( vm ) ;
Config config( vm ) ;
Log( "config file name %1%", config.Filename(), log::verbose );
std::unique_ptr<http::Agent> http( new http::CurlAgent );
if ( vm.count( "log-http" ) )
http->SetLog( new http::ResponseLog( vm["log-http"].as<std::string>(), ".txt" ) );
std::unique_ptr<ProgressBar> pb;
if ( vm.count( "progress-bar" ) )
{
pb.reset( new ProgressBar() );
http->SetProgressReporter( pb.get() );
}
if ( vm.count( "auth" ) )
{
std::string id = vm.count( "id" ) > 0
? vm["id"].as<std::string>()
: default_id ;
std::string secret = vm.count( "secret" ) > 0
? vm["secret"].as<std::string>()
: default_secret ;
OAuth2 token( http.get(), id, secret ) ;
if ( vm.count("print-url") )
{
std::cout << token.MakeAuthURL() << std::endl ;
return 0 ;
}
std::cout
<< "-----------------------\n"
<< "Please open this URL in your browser to authenticate Grive2:\n\n"
<< token.MakeAuthURL()
<< std::endl ;
if ( !token.GetCode() )
{
std::cout << "Authentication failed\n";
return -1;
}
// save to config
config.Set( "id", Val( id ) ) ;
config.Set( "secret", Val( secret ) ) ;
config.Set( "refresh_token", Val( token.RefreshToken() ) ) ;
config.Save() ;
}
std::string refresh_token ;
std::string id ;
std::string secret ;
try
{
refresh_token = config.Get("refresh_token").Str() ;
id = config.Get("id").Str() ;
secret = config.Get("secret").Str() ;
}
catch ( Exception& e )
{
Log(
"Please run grive with the \"-a\" option if this is the "
"first time you're accessing your Google Drive!",
log::critical ) ;
return -1;
}
OAuth2 token( http.get(), refresh_token, id, secret ) ;
AuthAgent agent( token, http.get() ) ;
v2::Syncer2 syncer( &agent );
if ( vm.count( "upload-speed" ) > 0 )
agent.SetUploadSpeed( vm["upload-speed"].as<unsigned>() * 1000 );
if ( vm.count( "download-speed" ) > 0 )
agent.SetDownloadSpeed( vm["download-speed"].as<unsigned>() * 1000 );
Drive drive( &syncer, config.GetAll() ) ;
drive.DetectChanges() ;
if ( vm.count( "dry-run" ) == 0 )
{
// The progress bar should just be enabled when actual file transfers take place
if ( pb )
pb->setShowProgressBar( true ) ;
drive.Update() ;
if ( pb )
pb->setShowProgressBar( false ) ;
drive.SaveState() ;
}
else
drive.DryRun() ;
config.Save() ;
Log( "Finished!", log::info ) ;
return 0 ;
}
int main( int argc, char **argv )
{
using namespace gr ;
Json config = ReadConfig() ;
int c ;
while ((c = getopt (argc, argv, "ac:")) != -1)
{
switch ( c )
{
case 'a' :
{
std::cout
<< "-----------------------\n"
<< "Please go to this URL and get an authenication code:\n\n"
<< OAuth2::MakeAuthURL( client_id )
<< std::endl ;
std::cout
<< "\n-----------------------\n"
<< "Please input the authenication code here" << std::endl ;
std::string code ;
std::cin >> code ;
OAuth2 token( client_id, client_secret ) ;
token.Auth( code ) ;
// save to config
config.Add( "refresh_token", Json( token.RefreshToken() ) ) ;
assert( config["refresh_token"].As<std::string>() == token.RefreshToken() ) ;
SaveConfig( config ) ;
break ;
}
}
}
std::string refresh_token ;
try
{
refresh_token = config["refresh_token"].As<std::string>() ;
return Main( argc, argv ) ;
}
catch ( const std::runtime_error& error )
catch ( Exception& e )
{
std::cerr << "Please run grive with the \"-a\" option if this is the "
<< "first time you're accessing your Google Drive!\n";
return -1;
Log( "exception: %1%", boost::diagnostic_information(e), log::critical ) ;
}
OAuth2 token( refresh_token, client_id, client_secret ) ;
Drive drive( token ) ;
return 0 ;
catch ( std::exception& e )
{
Log( "exception: %1%", e.what(), log::critical ) ;
}
catch ( ... )
{
Log( "unexpected exception", log::critical ) ;
}
return -1 ;
}

View File

@ -2,22 +2,46 @@ project(libgrive)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
find_package(OpenSSL REQUIRED)
find_package(JSONC REQUIRED)
find_package(LibGcrypt REQUIRED)
find_package(CURL REQUIRED)
find_package(Backtrace)
find_package(Boost 1.40.0 COMPONENTS program_options filesystem unit_test_framework regex system REQUIRED)
find_package(BFD)
find_package(CppUnit)
find_package(Iberty)
find_package(PkgConfig)
pkg_check_modules(YAJL REQUIRED yajl)
add_definitions(-Wall)
# additional headers if build unit tests
IF ( CPPUNIT_FOUND )
set( OPT_INCS ${CPPUNIT_INCLUDE_DIR} )
ENDIF ( CPPUNIT_FOUND )
# build bfd classes if libbfd and the backtrace library is found
if ( BFD_FOUND AND Backtrace_FOUND )
set( OPT_LIBS ${DL_LIBRARY} ${BFD_LIBRARY} ${Backtrace_LIBRARY} )
file( GLOB OPT_SRC
src/bfd/*.cc
)
add_definitions( -DHAVE_BFD )
endif ( BFD_FOUND AND Backtrace_FOUND )
if ( IBERTY_FOUND )
set( OPT_LIBS ${OPT_LIBS} ${IBERTY_LIBRARY} )
else ( IBERTY_FOUND )
set( IBERTY_LIBRARY "" )
endif ( IBERTY_FOUND )
include_directories(
${libgrive_SOURCE_DIR}/src
${libgrive_SOURCE_DIR}/test
${Boost_INCLUDE_DIRS}
${OPT_INCS}
)
file(GLOB DRIVE_HEADERS
${libgrive_SOURCE_DIR}/src/drive/*.hh
${YAJL_INCLUDE_DIRS}
)
file (GLOB PROTOCOL_HEADERS
@ -28,27 +52,58 @@ file (GLOB UTIL_HEADERS
${libgrive_SOURCE_DIR}/src/util/*.hh
)
file (GLOB LIBGRIVE_SRC
src/drive/*.cc
src/protocol/*.cc
src/util/*.cc
file (GLOB XML_HEADERS
${libgrive_SOURCE_DIR}/src/xml/*.hh
)
add_library( grive SHARED ${LIBGRIVE_SRC} )
file (GLOB LIBGRIVE_SRC
src/base/*.cc
src/drive2/*.cc
src/http/*.cc
src/protocol/*.cc
src/json/*.cc
src/util/*.cc
src/util/log/*.cc
)
add_definitions(
-DTEST_DATA="${libgrive_SOURCE_DIR}/test/data/"
-DSRC_DIR="${libgrive_SOURCE_DIR}/src"
)
#add_library( grive SHARED ${LIBGRIVE_SRC} ${OPT_SRC} )
add_library( grive STATIC ${LIBGRIVE_SRC} ${OPT_SRC} )
target_link_libraries( grive
${YAJL_LIBRARIES}
${CURL_LIBRARIES}
${JSONC_LIBRARY}
${OPENSSL_LIBRARIES}
${LIBGCRYPT_LIBRARIES}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${IBERTY_LIBRARY}
${OPT_LIBS}
)
set_target_properties(grive PROPERTIES
SOVERSION 0 VERSION 0.0.3
)
#set_target_properties(grive PROPERTIES
# SOVERSION 0 VERSION ${GRIVE_VERSION}
#)
install(TARGETS grive LIBRARY DESTINATION lib)
install(FILES ${DRIVE_HEADERS} DESTINATION include/grive/drive)
install(FILES ${PROTOCOL_HEADERS} DESTINATION include/grive/protocol)
#set_target_properties(grive grive-static PROPERTIES OUTPUT_NAME grive)
if ( LIB_INSTALL_DIR )
else()
set ( LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib )
endif()
# don't install libgrive for now
#install(TARGETS grive LIBRARY DESTINATION ${LIB_INSTALL_DIR} )
#install(TARGETS grive ARCHIVE DESTINATION ${LIB_INSTALL_DIR} )
#install(FILES ${DRIVE_HEADERS} DESTINATION include/grive/drive)
#install(FILES ${PROTOCOL_HEADERS} DESTINATION include/grive/protocol)
#install(FILES ${UTIL_HEADERS} DESTINATION include/grive/util)
#install(FILES ${XML_HEADERS} DESTINATION include/grive/xml)
IF ( CPPUNIT_FOUND )
message("-- Building unitary tests along with the library and the binary")
@ -56,6 +111,7 @@ IF ( CPPUNIT_FOUND )
# list of test source files here
file(GLOB TEST_SRC
test/base/*.cc
test/util/*.cc
)
@ -70,3 +126,24 @@ IF ( CPPUNIT_FOUND )
)
ENDIF ( CPPUNIT_FOUND )
file(GLOB BTEST_SRC
test/btest/*.cc
)
add_executable( btest ${BTEST_SRC} )
target_link_libraries( btest
grive
${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}
)
if ( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-c++11-narrowing" )
endif ( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" )
if ( WIN32 )
else ( WIN32 )
set_target_properties( btest
PROPERTIES COMPILE_FLAGS -DBOOST_TEST_DYN_LINK )
endif (WIN32)

130
libgrive/src/base/Drive.cc Normal file
View File

@ -0,0 +1,130 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Drive.hh"
#include "Entry.hh"
#include "Feed.hh"
#include "Syncer.hh"
#include "http/Agent.hh"
#include "util/Destroy.hh"
#include "util/log/Log.hh"
#include <boost/bind.hpp>
// standard C++ library
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <map>
// for debugging only
#include <iostream>
namespace gr {
Drive::Drive( Syncer *syncer, const Val& options ) :
m_syncer ( syncer ),
m_root ( options["path"].Str() ),
m_state ( m_root, options ),
m_options ( options )
{
assert( m_syncer ) ;
}
void Drive::FromRemote( const Entry& entry )
{
m_state.FromRemote( entry ) ;
}
void Drive::FromChange( const Entry& entry )
{
if ( entry.IsRemoved() )
Log( "file \"%1%\" represents a deletion, ignored", entry.Title(), log::verbose ) ;
// folders go directly
else
m_state.FromRemote( entry ) ;
}
void Drive::SaveState()
{
m_state.Write() ;
}
void Drive::DetectChanges()
{
Log( "Reading local directories", log::info ) ;
m_state.FromLocal( m_root ) ;
Log( "Reading remote server file list", log::info ) ;
std::unique_ptr<Feed> feed = m_syncer->GetAll() ;
while ( feed->GetNext( m_syncer->Agent() ) )
{
std::for_each(
feed->begin(), feed->end(),
boost::bind( &Drive::FromRemote, this, _1 ) ) ;
}
m_state.ResolveEntry() ;
}
// pull the changes feed
// FIXME: unused until Grive will use the feed-based sync instead of reading full tree
void Drive::ReadChanges()
{
long prev_stamp = m_state.ChangeStamp() ;
if ( prev_stamp != -1 )
{
Trace( "previous change stamp is %1%", prev_stamp ) ;
Log( "Detecting changes from last sync", log::info ) ;
std::unique_ptr<Feed> feed = m_syncer->GetChanges( prev_stamp+1 ) ;
while ( feed->GetNext( m_syncer->Agent() ) )
{
std::for_each(
feed->begin(), feed->end(),
boost::bind( &Drive::FromChange, this, _1 ) ) ;
}
}
}
void Drive::Update()
{
Log( "Synchronizing files", log::info ) ;
m_state.Sync( m_syncer, m_options ) ;
UpdateChangeStamp( ) ;
}
void Drive::DryRun()
{
Log( "Synchronizing files (dry-run)", log::info ) ;
m_state.Sync( NULL, m_options ) ;
}
void Drive::UpdateChangeStamp( )
{
// FIXME: we should go through the changes to see if it was really Grive to made that change
// maybe by recording the updated timestamp and compare it?
m_state.ChangeStamp( m_syncer->GetChangeStamp( m_state.ChangeStamp()+1 ) );
}
} // end of namespace gr

View File

@ -19,41 +19,45 @@
#pragma once
#include "Collection.hh"
#include "base/State.hh"
#include "json/Val.hh"
#include "util/Exception.hh"
#include <string>
#include <vector>
namespace gr {
class OAuth2 ;
class Json ;
class Syncer ;
class Entry ;
class State ;
class Drive
{
public :
typedef std::vector<Collection> FolderList ;
typedef std::vector<Collection>::iterator FolderListIterator ;
Drive( Syncer *syncer, const Val& options ) ;
public :
Drive( OAuth2& auth ) ;
~Drive( ) ;
private :
void UpdateFile( const Json& entry ) ;
void DetectChanges() ;
void Update() ;
void DryRun() ;
void SaveState() ;
void ConstructDirTree( const std::vector<Json>& entries ) ;
FolderListIterator FindFolder( const std::string& href ) ;
struct Error : virtual Exception {} ;
private :
OAuth2& m_auth ;
std::vector<std::string> m_http_hdr ;
std::string m_resume_link ;
void ReadChanges() ;
void FromRemote( const Entry& entry ) ;
void FromChange( const Entry& entry ) ;
void UpdateChangeStamp( ) ;
FolderList m_coll ;
Collection m_root ;
private :
Syncer *m_syncer ;
fs::path m_root ;
State m_state ;
Val m_options ;
} ;
} // end of namespace
} // end of namespace gr

129
libgrive/src/base/Entry.cc Normal file
View File

@ -0,0 +1,129 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Entry.hh"
#include "util/Crypt.hh"
#include "util/log/Log.hh"
#include "util/OS.hh"
#include "xml/Node.hh"
#include "xml/NodeSet.hh"
#include <algorithm>
#include <iterator>
namespace gr {
/// construct an entry for the root folder
Entry::Entry( ) :
m_title ( "." ),
m_is_dir ( true ),
m_resource_id ( "folder:root" ),
m_change_stamp ( -1 ),
m_is_removed ( false ),
m_size ( 0 )
{
}
const std::vector<std::string>& Entry::ParentHrefs() const
{
return m_parent_hrefs ;
}
std::string Entry::Title() const
{
return m_title ;
}
std::string Entry::Filename() const
{
return m_filename ;
}
bool Entry::IsDir() const
{
return m_is_dir ;
}
std::string Entry::MD5() const
{
return m_md5 ;
}
u64_t Entry::Size() const
{
return m_size ;
}
DateTime Entry::MTime() const
{
return m_mtime ;
}
std::string Entry::SelfHref() const
{
return m_self_href ;
}
std::string Entry::ParentHref() const
{
return m_parent_hrefs.empty() ? "" : m_parent_hrefs.front() ;
}
std::string Entry::ResourceID() const
{
return m_resource_id ;
}
std::string Entry::ETag() const
{
return m_etag ;
}
std::string Entry::ContentSrc() const
{
return m_content_src ;
}
bool Entry::IsEditable() const
{
return m_is_editable ;
}
long Entry::ChangeStamp() const
{
return m_change_stamp ;
}
bool Entry::IsChange() const
{
return m_change_stamp != -1 ;
}
bool Entry::IsRemoved() const
{
return m_is_removed ;
}
std::string Entry::Name() const
{
return !m_filename.empty() ? m_filename : m_title ;
}
} // end of namespace gr

View File

@ -0,0 +1,88 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/Types.hh"
#include "util/DateTime.hh"
#include "util/FileSystem.hh"
#include <iosfwd>
#include <string>
#include <vector>
namespace gr {
/*! \brief corresponds to an "entry" in the resource feed
This class is decodes an entry in the resource feed. It will stored the properties like
title, filename and ETag etc in member variables. Normally entries are created by
parsing the resource feed
*/
class Entry
{
public :
Entry( ) ;
std::string Title() const ;
std::string Filename() const ;
bool IsDir() const ;
std::string MD5() const ;
DateTime MTime() const ;
u64_t Size() const ;
std::string Name() const ;
std::string ResourceID() const ;
std::string ETag() const ;
std::string SelfHref() const ;
std::string ParentHref() const ;
std::string ContentSrc() const ;
bool IsEditable() const ;
long ChangeStamp() const ;
bool IsChange() const ;
bool IsRemoved() const ;
const std::vector<std::string>& ParentHrefs() const ;
protected :
std::string m_title ;
std::string m_filename ;
bool m_is_dir ;
std::string m_md5 ;
std::string m_etag ;
std::string m_resource_id ;
std::vector<std::string> m_parent_hrefs ;
std::string m_self_href ;
std::string m_content_src ;
bool m_is_editable ;
long m_change_stamp ;
DateTime m_mtime ;
bool m_is_removed ;
u64_t m_size ;
} ;
} // end of namespace gr

47
libgrive/src/base/Feed.cc Normal file
View File

@ -0,0 +1,47 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Feed.hh"
#include "Entry.hh"
#include "http/Agent.hh"
namespace gr {
Feed::Feed( const std::string &url ):
m_next( url )
{
}
Feed::~Feed()
{
}
Feed::iterator Feed::begin() const
{
return m_entries.begin() ;
}
Feed::iterator Feed::end() const
{
return m_entries.end() ;
}
} // end of namespace gr::v1

53
libgrive/src/base/Feed.hh Normal file
View File

@ -0,0 +1,53 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "base/Entry.hh"
#include <vector>
#include <string>
namespace gr {
namespace http
{
class Agent ;
}
class Feed
{
public :
typedef std::vector<Entry> Entries;
typedef std::vector<Entry>::const_iterator iterator;
public :
Feed( const std::string& url );
virtual bool GetNext( http::Agent *http ) = 0 ;
virtual ~Feed() = 0 ;
iterator begin() const ;
iterator end() const ;
protected :
Entries m_entries ;
std::string m_next ;
} ;
} // end of namespace gr

View File

@ -0,0 +1,801 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Resource.hh"
#include "ResourceTree.hh"
#include "Entry.hh"
#include "Syncer.hh"
#include "json/Val.hh"
#include "util/CArray.hh"
#include "util/Crypt.hh"
#include "util/log/Log.hh"
#include "util/OS.hh"
#include "util/File.hh"
#include "http/Error.hh"
#include <boost/exception/all.hpp>
#include <boost/filesystem.hpp>
#include <boost/bind.hpp>
#include <errno.h>
#include <cassert>
// for debugging
#include <iostream>
namespace gr {
/// default constructor creates the root folder
Resource::Resource( const fs::path& root_folder ) :
m_name ( root_folder.string() ),
m_kind ( "folder" ),
m_size ( 0 ),
m_id ( "folder:root" ),
m_href ( "root" ),
m_is_editable( true ),
m_parent ( 0 ),
m_state ( sync ),
m_json ( NULL ),
m_local_exists( true )
{
}
Resource::Resource( const std::string& name, const std::string& kind ) :
m_name ( name ),
m_kind ( kind ),
m_size ( 0 ),
m_is_editable( true ),
m_parent ( 0 ),
m_state ( unknown ),
m_json ( NULL ),
m_local_exists( false )
{
}
void Resource::SetState( State new_state )
{
// only the new and delete states need to be set recursively
assert(
new_state == remote_new || new_state == remote_deleted ||
new_state == local_new || new_state == local_deleted
) ;
m_state = new_state ;
std::for_each( m_child.begin(), m_child.end(),
boost::bind( &Resource::SetState, _1, new_state ) ) ;
}
void Resource::FromRemoteFolder( const Entry& remote )
{
fs::path path = Path() ;
if ( !remote.IsEditable() )
Log( "folder %1% is read-only", path, log::verbose ) ;
// already sync
if ( m_local_exists && m_kind == "folder" )
{
Log( "folder %1% is in sync", path, log::verbose ) ;
m_state = sync ;
}
else if ( m_local_exists && m_kind == "file" )
{
// TODO: handle type change
Log( "%1% changed from folder to file", path, log::verbose ) ;
m_state = sync ;
}
else if ( m_local_exists && m_kind == "bad" )
{
Log( "%1% inaccessible", path, log::verbose ) ;
m_state = sync ;
}
else if ( remote.MTime().Sec() > m_mtime.Sec() ) // FIXME only seconds are stored in local index
{
// remote folder created after last sync, so remote is newer
Log( "folder %1% is created in remote", path, log::verbose ) ;
SetState( remote_new ) ;
}
else
{
Log( "folder %1% is deleted in local", path, log::verbose ) ;
SetState( local_deleted ) ;
}
}
/// Update the state according to information (i.e. Entry) from remote. This function
/// compares the modification time and checksum of both copies and determine which
/// one is newer.
void Resource::FromRemote( const Entry& remote )
{
// sync folder
if ( remote.IsDir() && IsFolder() )
FromRemoteFolder( remote ) ;
else
FromRemoteFile( remote ) ;
AssignIDs( remote ) ;
assert( m_state != unknown ) ;
if ( m_state == remote_new || m_state == remote_changed )
m_md5 = remote.MD5() ;
m_mtime = remote.MTime() ;
}
void Resource::AssignIDs( const Entry& remote )
{
// the IDs from change feed entries are different
if ( !remote.IsChange() )
{
m_id = remote.ResourceID() ;
m_href = remote.SelfHref() ;
m_content = remote.ContentSrc() ;
m_is_editable = remote.IsEditable() ;
m_etag = remote.ETag() ;
m_md5 = remote.MD5() ;
}
}
void Resource::FromRemoteFile( const Entry& remote )
{
assert( m_parent != 0 ) ;
fs::path path = Path() ;
// recursively create/delete folder
if ( m_parent->m_state == remote_new || m_parent->m_state == remote_deleted ||
m_parent->m_state == local_new || m_parent->m_state == local_deleted )
{
Log( "file %1% parent %2% recursively in %3% (%4%)", path,
( m_parent->m_state == remote_new || m_parent->m_state == local_new ) ? "created" : "deleted",
( m_parent->m_state == remote_new || m_parent->m_state == remote_deleted ) ? "remote" : "local",
m_parent->m_state, log::verbose ) ;
m_state = m_parent->m_state ;
}
else if ( m_kind == "bad" )
{
m_state = sync;
}
// local not exists
else if ( !m_local_exists )
{
Trace( "file %1% change stamp = %2%", Path(), remote.ChangeStamp() ) ;
if ( remote.MTime().Sec() > m_mtime.Sec() || remote.MD5() != m_md5 || remote.ChangeStamp() > 0 )
{
Log( "file %1% is created in remote (change %2%)", path,
remote.ChangeStamp(), log::verbose ) ;
m_size = remote.Size();
m_state = remote_new ;
}
else
{
Log( "file %1% is deleted in local", path, log::verbose ) ;
m_state = local_deleted ;
}
}
// remote checksum unknown, assume the file is not changed in remote
else if ( remote.MD5().empty() )
{
Log( "file %1% has unknown checksum in remote. assumed in sync",
Path(), log::verbose ) ;
m_state = sync ;
}
// use mtime to check which one is more recent
else if ( remote.Size() != m_size || remote.MD5() != GetMD5() )
{
assert( m_state != unknown ) ;
// if remote is modified
if ( remote.MTime().Sec() > m_mtime.Sec() )
{
Log( "file %1% is changed in remote", path, log::verbose ) ;
m_size = remote.Size();
m_state = remote_changed ;
}
// remote also has the file, so it's not new in local
else if ( m_state == local_new || m_state == remote_deleted )
{
Log( "file %1% is changed in local", path, log::verbose ) ;
m_state = local_changed ;
}
else
Trace( "file %1% state is %2%", m_name, m_state ) ;
}
// if checksum is equal, no need to compare the mtime
else
{
Log( "file %1% is already in sync", Path(), log::verbose ) ;
m_state = sync ;
}
}
void Resource::FromDeleted( Val& state )
{
assert( !m_json );
m_json = &state;
if ( state.Has( "ctime" ) )
m_ctime.Assign( state["ctime"].U64(), 0 );
if ( state.Has( "md5" ) )
m_md5 = state["md5"];
if ( state.Has( "srv_time" ) )
m_mtime.Assign( state[ "srv_time" ].U64(), 0 ) ;
if ( state.Has( "size" ) )
m_size = state[ "size" ].U64();
m_state = both_deleted;
}
/// Update the resource with the attributes of local file or directory. This
/// function will propulate the fields in m_entry.
void Resource::FromLocal( Val& state )
{
assert( !m_json );
m_json = &state;
// root folder is always in sync
if ( !IsRoot() )
{
fs::path path = Path() ;
FileType ft ;
try
{
os::Stat( path, &m_ctime, (off64_t*)&m_size, &ft ) ;
}
catch ( os::Error &e )
{
// invalid symlink, unreadable file or something else
int const* eno = boost::get_error_info< boost::errinfo_errno >(e);
Log( "Error accessing %1%: %2%; skipping file", path.string(), strerror( *eno ), log::warning );
m_state = sync;
m_kind = "bad";
return;
}
if ( ft == FT_UNKNOWN )
{
// Skip sockets/FIFOs/etc
Log( "File %1% is not a regular file or directory; skipping file", path.string(), log::warning );
m_state = sync;
m_kind = "bad";
return;
}
m_name = path.filename().string() ;
m_kind = ft == FT_DIR ? "folder" : "file";
m_local_exists = true;
bool is_changed;
if ( state.Has( "ctime" ) && (u64_t) m_ctime.Sec() <= state["ctime"].U64() &&
( ft == FT_DIR || state.Has( "md5" ) ) )
{
if ( ft != FT_DIR )
m_md5 = state["md5"];
is_changed = false;
}
else
{
if ( ft != FT_DIR )
{
// File is changed locally. TODO: Detect conflicts
is_changed = ( state.Has( "size" ) && m_size != state["size"].U64() ) ||
!state.Has( "md5" ) || GetMD5() != state["md5"].Str();
}
else
is_changed = true;
}
if ( state.Has( "srv_time" ) )
m_mtime.Assign( state[ "srv_time" ].U64(), 0 ) ;
// Upload file if it is changed and remove if not.
// State will be updated to sync/remote_changed in FromRemote()
m_state = is_changed ? local_new : remote_deleted;
if ( m_state == local_new )
{
// local_new means this file is changed in local.
// this means we can't delete any of its parents.
// make sure their state is also set to local_new.
Resource *p = m_parent;
while ( p && p->m_state == remote_deleted )
{
p->m_state = local_new;
p = p->m_parent;
}
}
}
assert( m_state != unknown ) ;
}
std::string Resource::SelfHref() const
{
return m_href ;
}
std::string Resource::ContentSrc() const
{
return m_content ;
}
std::string Resource::ETag() const
{
return m_etag ;
}
std::string Resource::Name() const
{
return m_name ;
}
std::string Resource::Kind() const
{
return m_kind ;
}
DateTime Resource::ServerTime() const
{
return m_mtime ;
}
std::string Resource::ResourceID() const
{
return m_id ;
}
Resource::State Resource::GetState() const
{
return m_state ;
}
const Resource* Resource::Parent() const
{
assert( m_parent == 0 || m_parent->IsFolder() ) ;
return m_parent ;
}
Resource* Resource::Parent()
{
assert( m_parent == 0 || m_parent->IsFolder() ) ;
return m_parent ;
}
void Resource::AddChild( Resource *child )
{
assert( child != 0 ) ;
assert( child->m_parent == 0 || child->m_parent == this ) ;
assert( child != this ) ;
child->m_parent = this ;
m_child.push_back( child ) ;
}
bool Resource::IsFolder() const
{
return m_kind == "folder" ;
}
bool Resource::IsEditable() const
{
return m_is_editable ;
}
fs::path Resource::Path() const
{
assert( m_parent != this ) ;
assert( m_parent == 0 || m_parent->IsFolder() ) ;
return m_parent != 0 ? (m_parent->Path() / m_name) : m_name ;
}
// Path relative to the root directory
fs::path Resource::RelPath() const
{
assert( m_parent != this ) ;
assert( m_parent == 0 || m_parent->IsFolder() ) ;
return m_parent != 0 && !m_parent->IsRoot() ? (m_parent->RelPath() / m_name) : m_name ;
}
bool Resource::IsInRootTree() const
{
assert( m_parent == 0 || m_parent->IsFolder() ) ;
return m_parent == 0 ? IsRoot() : m_parent->IsInRootTree() ;
}
Resource* Resource::FindChild( const std::string& name )
{
for ( std::vector<Resource*>::iterator i = m_child.begin() ; i != m_child.end() ; ++i )
{
assert( (*i)->m_parent == this ) ;
if ( (*i)->m_name == name )
return *i ;
}
return 0 ;
}
// try to change the state to "sync"
void Resource::Sync( Syncer *syncer, ResourceTree *res_tree, const Val& options )
{
assert( m_state != unknown ) ;
assert( !IsRoot() || m_state == sync ) ; // root folder is already synced
try
{
SyncSelf( syncer, res_tree, options ) ;
}
catch ( File::Error &e )
{
int *en = boost::get_error_info< boost::errinfo_errno > ( e ) ;
Log( "Error syncing %1%: %2%", Path(), en ? strerror( *en ) : "", log::error );
return;
}
catch ( boost::filesystem::filesystem_error &e )
{
Log( "Error syncing %1%: %2%", Path(), e.what(), log::error );
return;
}
catch ( http::Error &e )
{
int *curlcode = boost::get_error_info< http::CurlCode > ( e ) ;
int *httpcode = boost::get_error_info< http::HttpResponseCode > ( e ) ;
std::string msg;
if ( curlcode )
msg = *( boost::get_error_info< http::CurlErrMsg > ( e ) );
else if ( httpcode )
msg = "HTTP " + boost::to_string( *httpcode );
else
msg = e.what();
Log( "Error syncing %1%: %2%", Path(), msg, log::error );
std::string *url = boost::get_error_info< http::Url > ( e );
std::string *resp_hdr = boost::get_error_info< http::HttpResponseHeaders > ( e );
std::string *resp_txt = boost::get_error_info< http::HttpResponseText > ( e );
http::Header *req_hdr = boost::get_error_info< http::HttpRequestHeaders > ( e );
if ( url )
Log( "Request URL: %1%", *url, log::verbose );
if ( req_hdr )
Log( "Request headers: %1%", req_hdr->Str(), log::verbose );
if ( resp_hdr )
Log( "Response headers: %1%", *resp_hdr, log::verbose );
if ( resp_txt )
Log( "Response text: %1%", *resp_txt, log::verbose );
return;
}
// if myself is deleted, no need to do the childrens
if ( m_state != local_deleted && m_state != remote_deleted )
{
std::for_each( m_child.begin(), m_child.end(),
boost::bind( &Resource::Sync, _1, syncer, res_tree, options ) ) ;
}
}
bool Resource::CheckRename( Syncer* syncer, ResourceTree *res_tree )
{
if ( !IsFolder() && ( m_state == local_new || m_state == remote_new ) )
{
bool is_local = m_state == local_new;
State other = is_local ? local_deleted : remote_deleted;
if ( is_local )
{
// First check size index for locally added files
details::SizeRange moved = res_tree->FindBySize( m_size );
bool found = false;
for ( details::SizeMap::iterator i = moved.first ; i != moved.second; i++ )
{
Resource *m = *i;
if ( m->m_state == other )
{
found = true;
break;
}
}
if ( !found )
{
// Don't check md5 sums if there are no deleted files with same size
return false;
}
}
details::MD5Range moved = res_tree->FindByMD5( GetMD5() );
for ( details::MD5Map::iterator i = moved.first ; i != moved.second; i++ )
{
Resource *m = *i;
if ( m->m_state == other )
{
Resource* from = m_state == local_new || m_state == remote_new ? m : this;
Resource* to = m_state == local_new || m_state == remote_new ? this : m;
Log( "sync %1% moved to %2%. moving %3%", from->Path(), to->Path(),
is_local ? "remote" : "local", log::info );
if ( syncer )
{
if ( is_local )
{
syncer->Move( from, to->Parent(), to->Name() );
to->SetIndex( false );
}
else
{
fs::rename( from->Path(), to->Path() );
to->SetIndex( true );
}
to->m_mtime = from->m_mtime;
to->m_json->Set( "srv_time", Val( from->m_mtime.Sec() ) );
from->DeleteIndex();
}
from->m_state = both_deleted;
to->m_state = sync;
return true;
}
}
}
return false;
}
void Resource::SyncSelf( Syncer* syncer, ResourceTree *res_tree, const Val& options )
{
assert( !IsRoot() || m_state == sync ) ; // root is always sync
assert( IsRoot() || !syncer || m_parent->IsFolder() ) ;
assert( IsRoot() || m_parent->m_state != remote_deleted ) ;
assert( IsRoot() || m_parent->m_state != local_deleted ) ;
const fs::path path = Path() ;
// Detect renames
if ( CheckRename( syncer, res_tree ) )
return;
switch ( m_state )
{
case local_new :
Log( "sync %1% doesn't exist in server, uploading", path, log::info ) ;
if ( syncer && syncer->Create( this ) )
{
m_state = sync ;
SetIndex( false );
}
break ;
case local_deleted :
Log( "sync %1% deleted in local. deleting remote", path, log::info ) ;
if ( syncer && !options["no-delete-remote"].Bool() )
{
syncer->DeleteRemote( this ) ;
DeleteIndex() ;
}
break ;
case local_changed :
Log( "sync %1% changed in local. uploading", path, log::info ) ;
if ( syncer && syncer->EditContent( this, options["new-rev"].Bool() ) )
{
m_state = sync ;
SetIndex( false );
}
break ;
case remote_new :
if ( options["no-remote-new"].Bool() )
Log( "sync %1% created in remote. skipping", path, log::info ) ;
else
{
Log( "sync %1% created in remote. creating local", path, log::info ) ;
if ( syncer )
{
if ( IsFolder() )
fs::create_directories( path ) ;
else
syncer->Download( this, path ) ;
SetIndex( true ) ;
m_state = sync ;
}
}
break ;
case remote_changed :
assert( !IsFolder() ) ;
if ( options["upload-only"].Bool() )
Log( "sync %1% changed in remote. skipping", path, log::info ) ;
else
{
Log( "sync %1% changed in remote. downloading", path, log::info ) ;
if ( syncer )
{
syncer->Download( this, path ) ;
SetIndex( true ) ;
m_state = sync ;
}
}
break ;
case remote_deleted :
Log( "sync %1% deleted in remote. deleting local", path, log::info ) ;
if ( syncer )
{
DeleteLocal() ;
DeleteIndex() ;
}
break ;
case both_deleted :
if ( syncer )
DeleteIndex() ;
break ;
case sync :
Log( "sync %1% already in sync", path, log::verbose ) ;
if ( !IsRoot() )
SetIndex( false ) ;
break ;
// shouldn't go here
case unknown :
default :
assert( false ) ;
break ;
}
if ( syncer && m_json )
{
// Update server time of this file
m_json->Set( "srv_time", Val( m_mtime.Sec() ) );
}
}
void Resource::SetServerTime( const DateTime& time )
{
m_mtime = time ;
}
/// this function doesn't really remove the local file. it renames it.
void Resource::DeleteLocal()
{
static const boost::format trash_file( "%1%-%2%" ) ;
assert( m_parent != NULL ) ;
Resource* p = m_parent;
fs::path destdir;
while ( !p->IsRoot() )
{
destdir = p->Name() / destdir;
p = p->Parent();
}
destdir = p->Path() / ".trash" / destdir;
fs::path dest = destdir / Name();
std::size_t idx = 1 ;
while ( fs::exists( dest ) && idx != 0 )
dest = destdir / (boost::format(trash_file) % Name() % idx++).str() ;
// wrap around! just remove the file
if ( idx == 0 )
fs::remove_all( Path() ) ;
else
{
fs::create_directories( dest.parent_path() ) ;
fs::rename( Path(), dest ) ;
}
}
void Resource::DeleteIndex()
{
(*m_parent->m_json)["tree"].Del( Name() );
m_json = NULL;
}
void Resource::SetIndex( bool re_stat )
{
assert( m_parent && m_parent->m_json != NULL );
if ( !m_json )
m_json = &((*m_parent->m_json)["tree"]).Item( Name() );
FileType ft;
if ( re_stat )
os::Stat( Path(), &m_ctime, NULL, &ft );
else
ft = IsFolder() ? FT_DIR : FT_FILE;
m_json->Set( "ctime", Val( m_ctime.Sec() ) );
if ( ft != FT_DIR )
{
m_json->Set( "md5", Val( m_md5 ) );
m_json->Set( "size", Val( m_size ) );
m_json->Del( "tree" );
}
else
{
// add tree item if it does not exist
m_json->Item( "tree" );
m_json->Del( "md5" );
m_json->Del( "size" );
}
}
Resource::iterator Resource::begin() const
{
return m_child.begin() ;
}
Resource::iterator Resource::end() const
{
return m_child.end() ;
}
std::size_t Resource::size() const
{
return m_child.size() ;
}
std::ostream& operator<<( std::ostream& os, Resource::State s )
{
static const char *state[] =
{
"sync", "local_new", "local_changed", "local_deleted", "remote_new",
"remote_changed", "remote_deleted", "both_deleted"
} ;
assert( s >= 0 && s < Count(state) ) ;
return os << state[s] ;
}
std::string Resource::StateStr() const
{
std::ostringstream ss ;
ss << m_state ;
return ss.str() ;
}
u64_t Resource::Size() const
{
return m_size ;
}
std::string Resource::MD5() const
{
return m_md5 ;
}
std::string Resource::GetMD5()
{
if ( m_md5.empty() && !IsFolder() && m_local_exists )
{
// MD5 checksum is calculated lazily and only when really needed:
// 1) when a local rename is supposed (when there are a new file and a deleted file of the same size)
// 2) when local ctime is changed, but file size isn't
m_md5 = crypt::MD5::Get( Path() );
}
return m_md5 ;
}
bool Resource::IsRoot() const
{
// Root entry does not show up in file feeds, so we check for empty parent (and self-href)
return !m_parent ;
}
bool Resource::HasID() const
{
return !m_href.empty() && !m_id.empty() ;
}
} // end of namespace

View File

@ -0,0 +1,173 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/Types.hh"
#include "util/DateTime.hh"
#include "util/Exception.hh"
#include "util/FileSystem.hh"
#include <string>
#include <vector>
#include <iosfwd>
namespace gr {
class ResourceTree ;
class Syncer ;
class Val ;
class Entry ;
/*! \brief A resource can be a file or a folder in the google drive
The google drive contains a number of resources, which is represented by this class.
It also contains linkage to other resources, such as parent and childrens.
*/
class Resource
{
public :
typedef std::vector<Resource*> Children ;
typedef Children::const_iterator iterator ;
/// State of the resource. indicating what to do with the resource
enum State
{
/// The best state: the file is the same in remote and in local.
sync,
/// Resource created in local, but remote does not have it.
/// We should create the resource in google drive and upload new content
local_new,
/// Resource exists in both local & remote, but changes in local is newer
/// than remote. We should upload local copy to overwrite remote.
local_changed,
/// Resource deleted from local since last time grive has checked.
local_deleted,
/// Resource created in google drive, but not exist in local.
/// We should download the file.
remote_new,
/// Resource exists in both local & remote, but remote is newer.
remote_changed,
/// Resource delete in remote, need to delete in local
remote_deleted,
/// Both deleted. State is used to remove leftover files from the index after sync.
both_deleted,
/// invalid value
unknown
} ;
public :
Resource(const fs::path& root_folder) ;
Resource( const std::string& name, const std::string& kind ) ;
bool IsFolder() const ;
bool IsEditable() const ;
std::string Name() const ;
std::string Kind() const ;
DateTime ServerTime() const ;
std::string SelfHref() const ;
std::string ContentSrc() const ;
std::string ETag() const ;
std::string ResourceID() const ;
State GetState() const;
const Resource* Parent() const ;
Resource* Parent() ;
void AddChild( Resource *child ) ;
Resource* FindChild( const std::string& title ) ;
fs::path Path() const ;
fs::path RelPath() const ;
bool IsInRootTree() const ;
bool IsRoot() const ;
bool HasID() const ;
u64_t Size() const;
std::string MD5() const ;
std::string GetMD5() ;
void FromRemote( const Entry& remote ) ;
void FromDeleted( Val& state ) ;
void FromLocal( Val& state ) ;
void Sync( Syncer* syncer, ResourceTree *res_tree, const Val& options ) ;
void SetServerTime( const DateTime& time ) ;
// children access
iterator begin() const ;
iterator end() const ;
std::size_t size() const ;
std::string StateStr() const ;
private :
void AssignIDs( const Entry& remote ) ;
friend std::ostream& operator<<( std::ostream& os, State s ) ;
friend class Syncer ;
private :
void SetState( State new_state ) ;
void FromRemoteFolder( const Entry& remote ) ;
void FromRemoteFile( const Entry& remote ) ;
void DeleteLocal() ;
void DeleteIndex() ;
void SetIndex( bool ) ;
bool CheckRename( Syncer* syncer, ResourceTree *res_tree ) ;
void SyncSelf( Syncer* syncer, ResourceTree *res_tree, const Val& options ) ;
private :
std::string m_name ;
std::string m_kind ;
std::string m_md5 ;
DateTime m_mtime ;
DateTime m_ctime ;
u64_t m_size ;
std::string m_id ;
std::string m_href ;
std::string m_content ;
std::string m_etag ;
bool m_is_editable ;
// not owned
Resource *m_parent ;
std::vector<Resource*> m_child ;
State m_state ;
Val* m_json ;
bool m_local_exists ;
} ;
} // end of namespace gr::v1

View File

@ -0,0 +1,158 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ResourceTree.hh"
#include "util/Destroy.hh"
#include "util/log/Log.hh"
#include <algorithm>
#include <cassert>
namespace gr {
using namespace details ;
ResourceTree::ResourceTree( const fs::path& rootFolder ) :
m_root( new Resource( rootFolder ) )
{
m_set.insert( m_root ) ;
}
ResourceTree::ResourceTree( const ResourceTree& fs ) :
m_root( 0 )
{
const Set& s = fs.m_set.get<ByIdentity>() ;
for ( Set::const_iterator i = s.begin() ; i != s.end() ; ++i )
{
Resource *c = new Resource( **i ) ;
if ( c->IsRoot() )
m_root = c ;
m_set.insert( c ) ;
}
assert( m_root != 0 ) ;
}
ResourceTree::~ResourceTree( )
{
Clear() ;
}
void ResourceTree::Clear()
{
// delete all pointers
const Set& s = m_set.get<ByIdentity>() ;
std::for_each( s.begin(), s.end(), Destroy() ) ;
m_set.clear() ;
m_root = 0 ;
}
Resource* ResourceTree::Root()
{
assert( m_root != 0 ) ;
return m_root ;
}
const Resource* ResourceTree::Root() const
{
assert( m_root != 0 ) ;
return m_root ;
}
Resource* ResourceTree::FindByHref( const std::string& href )
{
// for the resource that not yet have href (e.g. not yet fetched from server)
// their href will be empty.
if ( href.empty() )
return 0 ;
HrefMap& map = m_set.get<ByHref>() ;
HrefMap::iterator i = map.find( href ) ;
return i != map.end() ? *i : 0 ;
}
const Resource* ResourceTree::FindByHref( const std::string& href ) const
{
const HrefMap& map = m_set.get<ByHref>() ;
HrefMap::const_iterator i = map.find( href ) ;
return i != map.end() ? *i : 0 ;
}
MD5Range ResourceTree::FindByMD5( const std::string& md5 )
{
MD5Map& map = m_set.get<ByMD5>() ;
if ( !md5.empty() )
return map.equal_range( md5 );
return MD5Range( map.end(), map.end() ) ;
}
SizeRange ResourceTree::FindBySize( u64_t size )
{
SizeMap& map = m_set.get<BySize>() ;
return map.equal_range( size );
}
/// Reinsert should be called when the ID/HREF/MD5 were updated
bool ResourceTree::ReInsert( Resource *coll )
{
Set& s = m_set.get<ByIdentity>() ;
Set::iterator i = s.find( coll ) ;
if ( i != s.end() )
{
s.erase( i ) ;
m_set.insert( coll ) ;
return true ;
}
else
return false ;
}
void ResourceTree::Insert( Resource *coll )
{
m_set.insert( coll ) ;
}
void ResourceTree::Erase( Resource *coll )
{
Set& s = m_set.get<ByIdentity>() ;
s.erase( s.find( coll ) ) ;
}
void ResourceTree::Update( Resource *coll, const Entry& e )
{
assert( coll != 0 ) ;
coll->FromRemote( e ) ;
ReInsert( coll ) ;
}
ResourceTree::iterator ResourceTree::begin()
{
return m_set.get<ByIdentity>().begin() ;
}
ResourceTree::iterator ResourceTree::end()
{
return m_set.get<ByIdentity>().end() ;
}
} // end of namespace gr

View File

@ -0,0 +1,99 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "Resource.hh"
#include "util/FileSystem.hh"
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/mem_fun.hpp>
namespace gr {
namespace details
{
using namespace boost::multi_index ;
struct ByMD5 {} ;
struct ByHref {} ;
struct ByIdentity {} ;
struct BySize {} ;
typedef multi_index_container<
Resource*,
indexed_by<
hashed_non_unique<tag<ByHref>, const_mem_fun<Resource, std::string, &Resource::SelfHref> >,
hashed_non_unique<tag<ByMD5>, const_mem_fun<Resource, std::string, &Resource::MD5> >,
hashed_non_unique<tag<BySize>, const_mem_fun<Resource, u64_t, &Resource::Size> >,
hashed_unique<tag<ByIdentity>, identity<Resource*> >
>
> Folders ;
typedef Folders::index<ByMD5>::type MD5Map ;
typedef Folders::index<ByHref>::type HrefMap ;
typedef Folders::index<BySize>::type SizeMap ;
typedef Folders::index<ByIdentity>::type Set ;
typedef std::pair<SizeMap::iterator, SizeMap::iterator> SizeRange ;
typedef std::pair<MD5Map::iterator, MD5Map::iterator> MD5Range ;
}
/*! \brief A simple container for storing folders
This class stores a set of folders and provide fast search access from ID, HREF etc.
It is a wrapper around multi_index_container from Boost library.
*/
class ResourceTree
{
public :
typedef details::Set::iterator iterator ;
public :
ResourceTree( const fs::path& rootFolder ) ;
ResourceTree( const ResourceTree& fs ) ;
~ResourceTree( ) ;
Resource* FindByHref( const std::string& href ) ;
const Resource* FindByHref( const std::string& href ) const ;
details::MD5Range FindByMD5( const std::string& md5 ) ;
details::SizeRange FindBySize( u64_t size ) ;
bool ReInsert( Resource *coll ) ;
void Insert( Resource *coll ) ;
void Erase( Resource *coll ) ;
void Update( Resource *coll, const Entry& e ) ;
Resource* Root() ;
const Resource* Root() const ;
iterator begin() ;
iterator end() ;
private :
void Clear() ;
private :
details::Folders m_set ;
Resource* m_root ;
} ;
} // end of namespace gr

431
libgrive/src/base/State.cc Normal file
View File

@ -0,0 +1,431 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "State.hh"
#include "Entry.hh"
#include "Resource.hh"
#include "Syncer.hh"
#include "util/Crypt.hh"
#include "util/File.hh"
#include "util/log/Log.hh"
#include "json/JsonParser.hh"
#include <boost/algorithm/string.hpp>
#include <fstream>
namespace gr {
const std::string state_file = ".grive_state" ;
const std::string ignore_file = ".griveignore" ;
const int MAX_IGN = 65536 ;
const char* regex_escape_chars = ".^$|()[]{}*+?\\";
const boost::regex regex_escape_re( "[.^$|()\\[\\]{}*+?\\\\]" );
inline std::string regex_escape( std::string s )
{
return regex_replace( s, regex_escape_re, "\\\\&", boost::format_sed );
}
State::State( const fs::path& root, const Val& options ) :
m_root ( root ),
m_res ( options["path"].Str() ),
m_cstamp ( -1 )
{
Read() ;
// the "-f" option will make grive always think remote is newer
m_force = options.Has( "force" ) ? options["force"].Bool() : false ;
std::string m_orig_ign = m_ign;
if ( options.Has( "ignore" ) && options["ignore"].Str() != m_ign )
m_ign = options["ignore"].Str();
else if ( options.Has( "dir" ) )
{
const boost::regex trim_path( "^/+|/+$" );
std::string m_dir = regex_replace( options["dir"].Str(), trim_path, "" );
if ( !m_dir.empty() )
{
// "-s" is internally converted to an ignore regexp
m_dir = regex_escape( m_dir );
size_t pos = 0;
while ( ( pos = m_dir.find( '/', pos ) ) != std::string::npos )
{
m_dir = m_dir.substr( 0, pos ) + "$|" + m_dir;
pos = pos*2 + 3;
}
std::string ign = "^(?!"+m_dir+"(/|$))";
m_ign = ign;
}
}
m_ign_changed = m_orig_ign != "" && m_orig_ign != m_ign;
m_ign_re = boost::regex( m_ign.empty() ? "^\\.(grive$|grive_state$|trash)" : ( m_ign+"|^\\.(grive$|grive_state$|trash)" ) );
}
State::~State()
{
}
/// Synchronize local directory. Build up the resource tree from files and folders
/// of local directory.
void State::FromLocal( const fs::path& p )
{
m_res.Root()->FromLocal( m_st ) ;
FromLocal( p, m_res.Root(), m_st.Item( "tree" ) ) ;
}
bool State::IsIgnore( const std::string& filename )
{
return regex_search( filename.c_str(), m_ign_re, boost::format_perl );
}
void State::FromLocal( const fs::path& p, Resource* folder, Val& tree )
{
assert( folder != 0 ) ;
assert( folder->IsFolder() ) ;
Val::Object leftover = tree.AsObject();
for ( fs::directory_iterator i( p ) ; i != fs::directory_iterator() ; ++i )
{
std::string fname = i->path().filename().string() ;
std::string path = ( folder->IsRoot() ? fname : ( folder->RelPath() / fname ).string() );
if ( IsIgnore( path ) )
Log( "file %1% is ignored by grive", path, log::verbose ) ;
else
{
// if the Resource object of the child already exists, it should
// have been so no need to do anything here
Resource *c = folder->FindChild( fname ), *c2 = c ;
if ( !c )
{
c2 = new Resource( fname, "" ) ;
folder->AddChild( c2 ) ;
}
leftover.erase( fname );
Val& rec = tree.Item( fname );
if ( m_force )
rec.Del( "srv_time" );
c2->FromLocal( rec ) ;
if ( !c )
m_res.Insert( c2 ) ;
if ( c2->IsFolder() )
FromLocal( *i, c2, rec.Item( "tree" ) ) ;
}
}
for( Val::Object::iterator i = leftover.begin(); i != leftover.end(); i++ )
{
std::string path = folder->IsRoot() ? i->first : ( folder->RelPath() / i->first ).string();
if ( IsIgnore( path ) )
Log( "file %1% is ignored by grive", path, log::verbose ) ;
else
{
// Restore state of locally deleted files
Resource *c = folder->FindChild( i->first ), *c2 = c ;
if ( !c )
{
c2 = new Resource( i->first, i->second.Has( "tree" ) ? "folder" : "file" ) ;
folder->AddChild( c2 ) ;
}
Val& rec = tree.Item( i->first );
if ( m_force || m_ign_changed )
rec.Del( "srv_time" );
c2->FromDeleted( rec );
if ( !c )
m_res.Insert( c2 ) ;
}
}
}
void State::FromRemote( const Entry& e )
{
std::string fn = e.Filename() ;
std::string k = e.IsDir() ? "folder" : "file";
// common checkings
if ( !e.IsDir() && ( fn.empty() || e.ContentSrc().empty() ) )
Log( "%1% \"%2%\" is a google document, ignored", k, e.Name(), log::verbose ) ;
else if ( fn.find('/') != fn.npos )
Log( "%1% \"%2%\" contains a slash in its name, ignored", k, e.Name(), log::verbose ) ;
else if ( !e.IsChange() && e.ParentHrefs().size() != 1 )
Log( "%1% \"%2%\" has multiple parents, ignored", k, e.Name(), log::verbose ) ;
else if ( e.IsChange() )
FromChange( e ) ;
else if ( !Update( e ) )
m_unresolved.push_back( e ) ;
}
void State::ResolveEntry()
{
while ( !m_unresolved.empty() )
{
if ( TryResolveEntry() == 0 )
break ;
}
}
std::size_t State::TryResolveEntry()
{
assert( !m_unresolved.empty() ) ;
std::size_t count = 0 ;
std::list<Entry>& en = m_unresolved ;
for ( std::list<Entry>::iterator i = en.begin() ; i != en.end() ; )
{
if ( Update( *i ) )
{
i = en.erase( i ) ;
count++ ;
}
else
++i ;
}
return count ;
}
void State::FromChange( const Entry& e )
{
assert( e.IsChange() ) ;
// entries in the change feed is always treated as newer in remote,
// so we override the last sync time to 0
if ( Resource *res = m_res.FindByHref( e.SelfHref() ) )
m_res.Update( res, e ) ;
}
bool State::Update( const Entry& e )
{
assert( !e.IsChange() ) ;
assert( !e.ParentHref().empty() ) ;
if ( Resource *res = m_res.FindByHref( e.SelfHref() ) )
{
std::string path = res->RelPath().string();
if ( IsIgnore( path ) )
{
Log( "%1% is ignored by grive", path, log::verbose ) ;
return true;
}
m_res.Update( res, e ) ;
return true;
}
else if ( Resource *parent = m_res.FindByHref( e.ParentHref() ) )
{
if ( !parent->IsFolder() )
{
// https://github.com/vitalif/grive2/issues/148
Log( "%1% is owned by something that's not a directory: href=%2% name=%3%", e.Name(), e.ParentHref(), parent->RelPath(), log::error );
return true;
}
assert( parent->IsFolder() ) ;
std::string path = parent->IsRoot() ? e.Name() : ( parent->RelPath() / e.Name() ).string();
if ( IsIgnore( path ) )
{
Log( "%1% is ignored by grive", path, log::verbose ) ;
return true;
}
// see if the entry already exist in local
std::string name = e.Name() ;
Resource *child = parent->FindChild( name ) ;
if ( child )
{
// since we are updating the ID and Href, we need to remove it and re-add it.
m_res.Update( child, e ) ;
}
// folder entry exist in google drive, but not local. we should create
// the directory
else if ( e.IsDir() || !e.Filename().empty() )
{
// first create a dummy resource and update it later
child = new Resource( name, e.IsDir() ? "folder" : "file" ) ;
parent->AddChild( child ) ;
m_res.Insert( child ) ;
// update the state of the resource
m_res.Update( child, e ) ;
}
return true ;
}
else
return false ;
}
Resource* State::FindByHref( const std::string& href )
{
return m_res.FindByHref( href ) ;
}
State::iterator State::begin()
{
return m_res.begin() ;
}
State::iterator State::end()
{
return m_res.end() ;
}
void State::Read()
{
try
{
File st_file( m_root / state_file ) ;
m_st = ParseJson( st_file );
m_cstamp = m_st["change_stamp"].Int() ;
}
catch ( Exception& )
{
}
try
{
File ign_file( m_root / ignore_file ) ;
char ign[MAX_IGN] = { 0 };
int s = ign_file.Read( ign, MAX_IGN-1 ) ;
ParseIgnoreFile( ign, s );
}
catch ( Exception& e )
{
}
}
std::vector<std::string> split( const boost::regex& re, const char* str, int len )
{
std::vector<std::string> vec;
boost::cregex_token_iterator i( str, str+len, re, -1, boost::format_perl );
boost::cregex_token_iterator j;
while ( i != j )
{
vec.push_back( *i++ );
}
return vec;
}
bool State::ParseIgnoreFile( const char* buffer, int size )
{
const boost::regex re1( "([^\\\\]|^)[\\t\\r ]+$" );
const boost::regex re2( "^[\\t\\r ]+" );
const boost::regex re4( "([^\\\\](\\\\\\\\)*|^)\\\\\\*" );
const boost::regex re5( "([^\\\\](\\\\\\\\)*|^)\\\\\\?" );
std::string exclude_re, include_re;
std::vector<std::string> lines = split( boost::regex( "[\\n\\r]+" ), buffer, size );
for ( int i = 0; i < (int)lines.size(); i++ )
{
std::string str = regex_replace( regex_replace( lines[i], re1, "$1" ), re2, "" );
if ( str[0] == '#' || !str.size() )
{
continue;
}
bool inc = str[0] == '!';
if ( inc )
{
str = str.substr( 1 );
}
std::vector<std::string> parts = split( boost::regex( "/+" ), str.c_str(), str.size() );
for ( int j = 0; j < (int)parts.size(); j++ )
{
if ( parts[j] == "**" )
{
parts[j] = ".*";
}
else if ( parts[j] == "*" )
{
parts[j] = "[^/]*";
}
else
{
parts[j] = regex_escape( parts[j] );
std::string str1;
while (1)
{
str1 = regex_replace( parts[j], re5, "$1[^/]", boost::format_perl );
str1 = regex_replace( str1, re4, "$1[^/]*", boost::format_perl );
if ( str1.size() == parts[j].size() )
break;
parts[j] = str1;
}
}
}
if ( !inc )
{
str = boost::algorithm::join( parts, "/" ) + "(/|$)";
exclude_re = exclude_re + ( exclude_re.size() > 0 ? "|" : "" ) + str;
}
else
{
str = "";
std::string cur;
for ( int j = 0; j < (int)parts.size(); j++ )
{
cur = cur.size() > 0 ? cur + "/" + parts[j] : "^" + parts[j];
str = ( str.size() > 0 ? str + "|" + cur : cur ) + ( j < (int)parts.size()-1 ? "$" : "(/|$)" );
}
include_re = include_re + ( include_re.size() > 0 ? "|" : "" ) + str;
}
}
if ( exclude_re.size() > 0 )
{
m_ign = "^" + ( include_re.size() > 0 ? "(?!" + include_re + ")" : std::string() ) + "(" + exclude_re + ")$";
return true;
}
return false;
}
void State::Write()
{
m_st.Set( "change_stamp", Val( m_cstamp ) ) ;
m_st.Set( "ignore_regexp", Val( m_ign ) ) ;
fs::path filename = m_root / state_file ;
std::ofstream fs( filename.string().c_str() ) ;
fs << m_st ;
}
void State::Sync( Syncer *syncer, const Val& options )
{
// set the last sync time to the time on the client
m_res.Root()->Sync( syncer, &m_res, options ) ;
}
long State::ChangeStamp() const
{
return m_cstamp ;
}
void State::ChangeStamp( long cstamp )
{
Log( "change stamp is set to %1%", cstamp, log::verbose ) ;
m_cstamp = cstamp ;
}
} // end of namespace gr

View File

@ -0,0 +1,88 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ResourceTree.hh"
#include "util/DateTime.hh"
#include "util/FileSystem.hh"
#include "json/Val.hh"
#include <memory>
#include <boost/regex.hpp>
namespace gr {
class Entry ;
class Syncer ;
class Resource ;
class State
{
public :
typedef ResourceTree::iterator iterator ;
public :
explicit State( const fs::path& root, const Val& options ) ;
~State() ;
void FromLocal( const fs::path& p ) ;
void FromRemote( const Entry& e ) ;
void ResolveEntry() ;
void Read() ;
void Write() ;
Resource* FindByHref( const std::string& href ) ;
Resource* FindByID( const std::string& id ) ;
void Sync( Syncer *syncer, const Val& options ) ;
iterator begin() ;
iterator end() ;
long ChangeStamp() const ;
void ChangeStamp( long cstamp ) ;
private :
bool ParseIgnoreFile( const char* buffer, int size ) ;
void FromLocal( const fs::path& p, Resource *folder, Val& tree ) ;
void FromChange( const Entry& e ) ;
bool Update( const Entry& e ) ;
std::size_t TryResolveEntry() ;
bool IsIgnore( const std::string& filename ) ;
private :
fs::path m_root ;
ResourceTree m_res ;
int m_cstamp ;
std::string m_ign ;
boost::regex m_ign_re ;
Val m_st ;
bool m_force ;
bool m_ign_changed ;
std::list<Entry> m_unresolved ;
} ;
} // end of namespace gr

View File

@ -0,0 +1,59 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Syncer.hh"
#include "Resource.hh"
#include "Entry.hh"
#include "http/Agent.hh"
#include "http/Header.hh"
#include "http/Download.hh"
#include "util/OS.hh"
#include "util/log/Log.hh"
namespace gr {
Syncer::Syncer( http::Agent *http ):
m_http( http )
{
}
http::Agent* Syncer::Agent() const
{
return m_http;
}
void Syncer::Download( Resource *res, const fs::path& file )
{
http::Download dl( file.string(), http::Download::NoChecksum() ) ;
long r = m_http->Get( res->ContentSrc(), &dl, http::Header(), res->Size() ) ;
if ( r <= 400 )
{
if ( res->ServerTime() != DateTime() )
os::SetFileTime( file, res->ServerTime() ) ;
else
Log( "encountered zero date time after downloading %1%", file, log::warning ) ;
}
}
void Syncer::AssignIDs( Resource *res, const Entry& remote )
{
res->AssignIDs( remote );
}
} // end of namespace gr

View File

@ -0,0 +1,71 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/FileSystem.hh"
#include <string>
#include <vector>
#include <iosfwd>
namespace gr {
namespace http
{
class Agent ;
}
class DateTime ;
class Resource ;
class Entry ;
class Feed ;
/*! \brief A Syncer incapsulates all resource-related upload/download/edit methods */
class Syncer
{
public :
Syncer( http::Agent *http );
http::Agent* Agent() const;
virtual void DeleteRemote( Resource *res ) = 0;
virtual void Download( Resource *res, const fs::path& file );
virtual bool EditContent( Resource *res, bool new_rev ) = 0;
virtual bool Create( Resource *res ) = 0;
virtual bool Move( Resource* res, Resource* newParent, std::string newFilename ) = 0;
virtual std::unique_ptr<Feed> GetFolders() = 0;
virtual std::unique_ptr<Feed> GetAll() = 0;
virtual std::unique_ptr<Feed> GetChanges( long min_cstamp ) = 0;
virtual long GetChangeStamp( long min_cstamp ) = 0;
protected:
http::Agent *m_http;
void AssignIDs( Resource *res, const Entry& remote );
} ;
} // end of namespace gr

25
libgrive/src/bfd/Addr.hh Normal file
View File

@ -0,0 +1,25 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2006 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
namespace gr
{
typedef void* addr_t ;
}

View File

@ -0,0 +1,72 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Backtrace.hh"
#include "SymbolInfo.hh"
#include "util/CArray.hh"
#include <algorithm>
#include <cstring>
#include <sstream>
namespace gr {
Backtrace::Backtrace( std::size_t skip ) :
m_count( SymbolInfo::Instance()->Backtrace(m_stack, Count(m_stack) )),
m_skip( std::min( skip, m_count ) )
{
}
Backtrace::Backtrace( const Backtrace& bt ) :
m_count( bt.m_count ),
m_skip( bt.m_skip )
{
std::memcpy( m_stack, bt.m_stack, m_count * sizeof(m_stack[0]) ) ;
}
/*! \brief operator<< for printing backtraces
\internal
This function will call SymbolInfo::PrintTrace() to print out a backtrace
to the stream. It will use the SymbolInfo::Instance() singleton to get
the symbol information.
\param os The output stream.
\param b The backtrace information.
\sa SymbolInfo::Backtrace(), SymbolInfo::Instance()
*/
std::ostream& operator<<( std::ostream& os, const gr::Backtrace& b )
{
// the 1st function in the stack is SymbolInfo::Backtrace()
// the 2nd one is the Backtrace() constructor
// both are not interesting to user
for ( std::size_t i = b.m_skip ; i < b.m_count ; i++ )
SymbolInfo::Instance()->PrintTrace( b.m_stack[i], os, i - b.m_skip ) ;
return os ;
}
std::string Backtrace::ToString( ) const
{
std::ostringstream oss ;
oss << *this ;
return oss.str( ) ;
}
} // end of namespace

View File

@ -19,53 +19,38 @@
#pragma once
#include <string>
#include "protocol/HTTP.hh"
#include "util/DateTime.hh"
#include "Addr.hh"
#include <iosfwd>
#include <string>
namespace gr {
class Json ;
class OAuth2 ;
class Path ;
class File
/// A shortcut to print out backtrace information
/** \internal
The sole reason for this class to exists is to provide the
operator<< overload to allow:
\code
std::cout << Backtrace() << std::endl ;
\endcode
\sa SymbolInfo
*/
class Backtrace
{
public :
explicit File( const Path& file ) ;
explicit File( const Json& entry ) ;
explicit Backtrace( std::size_t skip = 2 ) ;
Backtrace( const Backtrace& bt ) ;
friend std::ostream& operator<<( std::ostream& os,
const gr::Backtrace& bt ) ;
std::string Title() const ;
std::string Filename() const ;
std::string Kind() const ;
std::string ServerMD5() const ;
DateTime ServerModified() const ;
std::string Href() const ;
std::string Parent() const ;
void Download( const Path& file, const http::Headers& auth ) const ;
void Upload( std::streambuf *file, const http::Headers& auth ) ;
std::string ToString( ) const ;
private :
void Update( const Json& entry ) ;
static std::string Parent( const Json& entry ) ;
private :
std::string m_title ;
std::string m_filename ;
std::string m_kind ;
std::string m_server_md5 ;
std::string m_etag ;
std::string m_href ;
std::string m_parent ;
std::string m_upload_link ;
DateTime m_server_modified ;
addr_t m_stack[100] ;
std::size_t m_count, m_skip ;
} ;
} // end of namespace

71
libgrive/src/bfd/Debug.cc Normal file
View File

@ -0,0 +1,71 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2006 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Debug.hh"
#include <cassert>
#ifdef __GNUC__
#include <cxxabi.h>
#endif
#include <cstdlib>
#include <ostream>
#include <iomanip>
namespace gr {
std::string Demangle( const char *name )
{
#ifdef __GNUC__
assert( name != 0 ) ;
char *cname = abi::__cxa_demangle( name, 0, 0, 0 ) ;
std::string result( name ) ;
if ( cname != 0 )
{
result = cname ;
std::free( cname ) ;
}
return result ;
#else
return std::string( ) ;
#endif
}
std::ostream& PrintHex( std::ostream& os, const void *buf, std::size_t size )
{
const unsigned char *raw = static_cast<const unsigned char*>( buf ) ;
for ( std::size_t i = 0 ; i < size ; i++ )
{
if ( i % 8 == 0 && i > 0 )
os << std::endl ;
if ( i % 8 == 0 )
os << std::hex << std::setw(8) << std::setfill('0') << i << " " ;
os << "0x" << std::hex << std::setw(2) << std::setfill('0')
<< (int)raw[i] << ", " ;
}
os << std::endl ;
return os ;
}
} // end of namespace

32
libgrive/src/bfd/Debug.hh Normal file
View File

@ -0,0 +1,32 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2006 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <cstddef>
#include <iosfwd>
#include <string>
namespace gr {
std::string Demangle( const char *name ) ;
std::ostream& PrintHex( std::ostream& os, const void *buf, std::size_t size ) ;
} // end of namespace

View File

@ -0,0 +1,194 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2006 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "SymbolInfo.hh"
#include "Debug.hh"
#include <vector>
#define PACKAGE "libgrive"
#include <bfd.h>
#include <execinfo.h>
#include <dlfcn.h>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <algorithm>
namespace gr {
struct SymbolInfo::Impl
{
bfd *m_bfd ;
asymbol **m_symbols ;
long m_symbol_count ;
} ;
SymbolInfo::SymbolInfo( )
: m_impl( new Impl )
{
m_impl->m_bfd = 0 ;
m_impl->m_symbols = 0 ;
m_impl->m_symbol_count = 0 ;
bfd_init( ) ;
// opening itself
bfd *b = bfd_openr( "/proc/self/exe", 0 ) ;
if ( b == NULL )
{
std::cerr << "cannot open executable: "
<< bfd_errmsg( bfd_get_error() ) << std::endl ;
return ;
}
if ( bfd_check_format( b, bfd_archive ) )
{
bfd_close( b ) ;
return ;
}
char **matching ;
if ( !bfd_check_format_matches( b, bfd_object, &matching ) )
{
std::cerr << "cannot read symbols from " << bfd_get_filename( b )
<< ": " << bfd_errmsg( bfd_get_error() ) << std::endl ;
if ( bfd_get_error( ) == bfd_error_file_ambiguously_recognized )
{
std::cerr << bfd_get_filename( b ) << ": Matching formats: " ;
for ( char **p = matching ; *p != 0 ; p++ )
std::cerr << " " << *p ;
std::cerr << std::endl ;
std::free( matching ) ;
}
bfd_close( b ) ;
return ;
}
m_impl->m_bfd = b ;
long storage_needed = bfd_get_symtab_upper_bound( m_impl->m_bfd ) ;
m_impl->m_symbols = (asymbol**)std::malloc( storage_needed ) ;
m_impl->m_symbol_count = bfd_canonicalize_symtab( b, m_impl->m_symbols ) ;
}
SymbolInfo::~SymbolInfo( )
{
if ( m_impl->m_symbols != 0 )
std::free( m_impl->m_symbols ) ;
}
struct SymbolInfo::BacktraceInfo
{
const SymbolInfo *m_pthis ;
void *m_addr ;
const char *m_filename ;
const char *m_func_name ;
unsigned int m_lineno ;
unsigned int m_is_found ;
static void Callback( bfd *abfd, asection *section, void* addr ) ;
} ;
void SymbolInfo::BacktraceInfo::Callback( bfd *abfd, asection *section,
void* data )
{
BacktraceInfo *info = (BacktraceInfo *)data ;
if ((section->flags & SEC_ALLOC) == 0)
return ;
// bfd_get_section_vma works up to 7b1cfbcf1a27951fb1b3a212995075dd6fdf985b,
// removed in 7c13bc8c91abf291f0206b6608b31955c5ea70d8 (binutils 2.33.1 or so)
// so it's substituted by its implementation to avoid checking for binutils
// version (which at least on Debian SID it's not that easy because the
// version.h is not included with the official package)
bfd_vma vma = section->vma;
unsigned long address = (unsigned long)(info->m_addr);
if ( address < vma )
return;
// bfd_section_size changed between the two objects described above,
// same rationale applies
bfd_size_type size = section->size;
if ( address > (vma + size))
return ;
const SymbolInfo *pthis = info->m_pthis ;
info->m_is_found = bfd_find_nearest_line( abfd, section,
pthis->m_impl->m_symbols,
address - vma,
&info->m_filename,
&info->m_func_name,
&info->m_lineno ) ;
}
std::size_t SymbolInfo::Backtrace( void **stack, std::size_t count )
{
std::size_t a = ::backtrace( stack, count ) ;
return a ;
}
void SymbolInfo::PrintTrace( void *addr, std::ostream& os, std::size_t idx )
{
BacktraceInfo btinfo =
{
this, addr, 0, 0, 0, false
} ;
Dl_info sym ;
bfd_map_over_sections( m_impl->m_bfd,
&SymbolInfo::BacktraceInfo::Callback,
&btinfo ) ;
if ( btinfo.m_is_found )
{
std::string filename = ( btinfo.m_filename == 0 ? std::string()
: btinfo.m_filename ) ;
#ifdef SRC_DIR
std::size_t pos = filename.find( SRC_DIR ) ;
if ( pos != std::string::npos )
filename.erase( pos, std::strlen( SRC_DIR ) ) ;
#endif
os << "#" << idx << " " << addr << " "
<< filename << ":" << btinfo.m_lineno
<< " "
<< (btinfo.m_func_name != 0 ? Demangle(btinfo.m_func_name) : "" )
<< std::endl ;
}
else if ( dladdr( addr, &sym ) )
os << "#" << idx << " " << addr << " "
<< sym.dli_fname
<< " "
<< (sym.dli_sname != 0 ? Demangle( sym.dli_sname ) : "" )
<< std::endl ;
}
SymbolInfo* SymbolInfo::Instance( )
{
static SymbolInfo sthis ;
return &sthis ;
}
} // end of namespace

View File

@ -0,0 +1,63 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2006 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "Addr.hh"
#include <memory>
#include <iosfwd>
namespace gr {
/// ource code symbolic information
/** \internal
This class represents symbolic information about the source code,
e.g. function names and line numbers. It provides an interface to
lookup these informations by address.
*/
class SymbolInfo
{
public :
SymbolInfo( ) ;
~SymbolInfo( ) ;
/*! \brief singleton function
\internal
Returns the SymbolInfo singleton. Normally only one object
of SymbolInfo is enough for one application, so a singleton
is enough. This function will create the SymbolInfo object
in the first call.
*/
static SymbolInfo* Instance( ) ;
std::size_t Backtrace( addr_t *stack, std::size_t count ) ;
void PrintTrace( addr_t addr, std::ostream& os, std::size_t idx = 0 ) ;
private :
struct Impl ;
const std::unique_ptr<Impl> m_impl ;
struct BacktraceInfo ;
friend struct BacktraceInfo ;
} ;
} // end of namespace

View File

@ -1,140 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Collection.hh"
#include "protocol/Json.hh"
#include "util/Path.hh"
#include "util/OS.hh"
#include <cassert>
// for debugging
#include <iostream>
namespace gr {
Collection::Collection( const Json& entry ) :
m_title ( entry["title"]["$t"].As<std::string>() ),
m_href ( entry["link"].FindInArray( "rel", "self" )["href"].As<std::string>() ),
m_parent ( 0 )
{
}
Collection::Collection(
const std::string& title,
const std::string& href ) :
m_title ( title ),
m_href ( href ),
m_parent( 0 )
{
}
std::string Collection::ParentHref( const Json& entry )
{
Json node ;
return entry["link"].FindInArray( "rel", "http://schemas.google.com/docs/2007#parent", node ) ?
node["href"].As<std::string>() : std::string() ;
}
const std::string& Collection::Href() const
{
return m_href ;
}
const std::string& Collection::Title() const
{
return m_title ;
}
const Collection* Collection::Parent() const
{
return m_parent ;
}
Collection* Collection::Parent()
{
return m_parent ;
}
void Collection::AddChild( Collection *child )
{
assert( child != 0 ) ;
assert( child->m_parent == 0 ) ;
assert( child != this ) ;
child->m_parent = this ;
m_child.push_back( child ) ;
}
void Collection::AddLeaf( const std::string& filename )
{
m_leaves.push_back( filename ) ;
}
bool Collection::IsCollection( const Json& entry )
{
Json node ;
return
entry["category"].FindInArray( "scheme", "http://schemas.google.com/g/2005#kind", node ) &&
node["label"].As<std::string>() == "folder" ;
}
void Collection::Swap( Collection& coll )
{
m_title.swap( coll.m_title ) ;
m_href.swap( coll.m_href ) ;
std::swap( m_parent, coll.m_parent ) ;
m_child.swap( coll.m_child ) ;
m_leaves.swap( coll.m_leaves ) ;
}
void Collection::CreateSubDir( const Path& prefix )
{
Path dir = prefix / m_title ;
os::MakeDir( dir ) ;
for ( std::vector<Collection*>::iterator i = m_child.begin() ; i != m_child.end() ; ++i )
{
assert( (*i)->m_parent == this ) ;
(*i)->CreateSubDir( dir ) ;
}
}
void Collection::ForEachFile(
Function<void(const std::string&)> callback,
const std::string& prefix )
{
}
Path Collection::Dir() const
{
assert( m_parent != this ) ;
return m_parent != 0 ? (m_parent->Dir() / m_title) : Path() ;
}
} // end of namespace
namespace std
{
void swap( gr::Collection& c1, gr::Collection& c2 )
{
c1.Swap( c2 ) ;
}
}

View File

@ -1,76 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/Function.hh"
#include <string>
#include <vector>
namespace gr {
class Json ;
class Path ;
class Collection
{
public :
explicit Collection( const Json& entry ) ;
Collection( const std::string& title, const std::string& href ) ;
// default copy ctor & op= are fine
static bool IsCollection( const Json& entry ) ;
static std::string ParentHref( const Json& entry ) ;
const std::string& Title() const ;
const std::string& Href() const ;
const Collection* Parent() const ;
Collection* Parent() ;
Path Dir() const ;
void AddChild( Collection *child ) ;
void AddLeaf( const std::string& filename ) ;
void Swap( Collection& coll ) ;
// traversing the tree
void CreateSubDir( const Path& prefix ) ;
void ForEachFile(
Function<void(const std::string&)> callback,
const std::string& prefix = "." ) ;
private :
std::string m_title ;
std::string m_href ;
// not owned
Collection *m_parent ;
std::vector<Collection*> m_child ;
std::vector<std::string> m_leaves ;
} ;
} // end of namespace
namespace std
{
void swap( gr::Collection& c1, gr::Collection& c2 ) ;
}

View File

@ -1,192 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Drive.hh"
#include "File.hh"
#include "protocol/HTTP.hh"
#include "protocol/Json.hh"
#include "protocol/OAuth2.hh"
#include "util/Crypt.hh"
#include "util/DateTime.hh"
#include "util/OS.hh"
#include "util/Path.hh"
// standard C++ library
#include <algorithm>
#include <cassert>
#include <fstream>
#include <map>
#include <sstream>
// for debugging only
#include <iostream>
namespace gr {
const std::string root_url = "https://docs.google.com/feeds/default/private/full" ;
Drive::Drive( OAuth2& auth ) :
m_auth( auth ),
m_root( ".", "https://docs.google.com/feeds/default/private/full/folder%3Aroot" )
{
m_http_hdr.push_back( "Authorization: Bearer " + m_auth.AccessToken() ) ;
m_http_hdr.push_back( "GData-Version: 3.0" ) ;
Json resp = Json::Parse( http::Get( root_url + "?alt=json&showfolders=true", m_http_hdr )) ;
Json resume_link ;
if ( resp["feed"]["link"].FindInArray( "rel", "http://schemas.google.com/g/2005#resumable-create-media", resume_link ) )
m_resume_link = resume_link["href"].As<std::string>() ;
Json::Array entries = resp["feed"]["entry"].As<Json::Array>() ;
ConstructDirTree( entries ) ;
for ( Json::Array::iterator i = entries.begin() ; i != entries.end() ; ++i )
{
if ( !Collection::IsCollection( *i ) )
{
UpdateFile( *i ) ;
}
}
}
Drive::~Drive( )
{
}
struct SortCollectionByHref
{
bool operator()( const Collection& c1, const Collection& c2 ) const
{
return c1.Href() < c2.Href() ;
}
} ;
Drive::FolderListIterator Drive::FindFolder( const std::string& href )
{
// try to find the parent by its href
std::vector<Collection>::iterator it =
std::lower_bound(
m_coll.begin(),
m_coll.end(),
Collection( "", href ),
SortCollectionByHref() ) ;
return (it != m_coll.end() && it->Href() == href) ? it : m_coll.end() ;
}
void Drive::ConstructDirTree( const std::vector<Json>& entries )
{
assert( m_coll.empty() ) ;
std::map<std::string, std::string> parent_href ;
// first, get all collections from the query result
for ( Json::Array::const_iterator i = entries.begin() ; i != entries.end() ; ++i )
{
if ( Collection::IsCollection( *i ) )
{
m_coll.push_back( Collection( *i ) ) ;
parent_href.insert(
std::make_pair(
m_coll.back().Href(),
Collection::ParentHref( *i ) ) ) ;
}
}
assert( m_coll.size() == parent_href.size() ) ;
// second, build up linkage between parent and child
std::sort( m_coll.begin(), m_coll.end(), SortCollectionByHref() ) ;
for ( FolderListIterator i = m_coll.begin() ; i != m_coll.end() ; ++i )
{
assert( parent_href.find( i->Href() ) != parent_href.end() ) ;
std::string parent = parent_href[i->Href()] ;
if ( parent.empty() )
m_root.AddChild( &*i ) ;
else
{
FolderListIterator pit = FindFolder( parent ) ;
if ( pit != m_coll.end() )
{
// it shouldn't happen, just in case
if ( &*i == &*pit )
std::cout
<< "the parent of folder " << i->Title()
<< " is itself. ignored" << std::endl ;
else
pit->AddChild( &*i ) ;
}
}
}
// lastly, iterating from the root, create the directories in the local file system
assert( m_root.Parent() == 0 ) ;
m_root.CreateSubDir( Path() ) ;
}
void Drive::UpdateFile( const Json& entry )
{
// only handle uploaded files
if ( entry.Has( "docs$suggestedFilename" ) )
{
File file( entry ) ;
bool changed = true ;
Path path = Path() / file.Filename() ;
// determine which folder the file belongs to
if ( !file.Parent().empty() )
{
FolderListIterator pit = FindFolder( file.Parent() ) ;
if ( pit != m_coll.end() )
path = pit->Dir() / file.Filename() ;
}
// compare checksum first if file exists
std::ifstream ifile( path.Str().c_str(), std::ios::binary | std::ios::in ) ;
if ( ifile && file.ServerMD5() == crypt::MD5(ifile.rdbuf()) )
changed = false ;
// if the checksum is different, file is changed and we need to update
if ( changed )
{
DateTime remote = file.ServerModified() ;
DateTime local = ifile ? os::FileMTime( path ) : DateTime() ;
// remote file is newer, download file
if ( !ifile || remote > local )
{
std::cout << "downloading " << path << std::endl ;
file.Download( path, m_http_hdr ) ;
}
else
{
std::cout << "local " << path << " is newer" << std::endl ;
// re-reading the file
ifile.seekg(0) ;
file.Upload( ifile.rdbuf(), m_http_hdr ) ;
}
}
}
}
} // end of namespace

View File

@ -1,153 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "File.hh"
#include "protocol/Json.hh"
#include "protocol/OAuth2.hh"
#include "util/OS.hh"
#include "util/Path.hh"
#include <algorithm>
#include <iterator>
#include <sstream>
namespace gr {
File::File( const Path& file )
{
}
File::File( const Json& entry )
{
Update( entry ) ;
}
void File::Update( const Json& entry )
{
m_title = entry["title"]["$t"].Str() ;
m_filename = entry["docs$suggestedFilename"]["$t"].Str() ;
m_href = entry["content"]["src"].Str() ;
m_parent = Parent( entry ) ;
m_server_modified = DateTime( entry["updated"]["$t"].Str() ) ;
m_etag = entry["gd$etag"].Str() ;
m_server_md5 = entry.Has("docs$md5Checksum") ?
entry["docs$md5Checksum"]["$t"].Str() : "" ;
Json node ;
m_kind = entry["category"].
FindInArray( "scheme", "http://schemas.google.com/g/2005#kind", node )
? node["label"].Str() : std::string() ;
m_upload_link = entry["link"].FindInArray( "rel",
"http://schemas.google.com/g/2005#resumable-edit-media" )["href"].Str() ;
// convert to lower case for easy comparison
std::transform(
m_server_md5.begin(),
m_server_md5.end(),
m_server_md5.begin(),
tolower ) ;
}
std::string File::Parent( const Json& entry )
{
Json node ;
return entry["link"].FindInArray( "rel", "http://schemas.google.com/docs/2007#parent", node ) ?
node["href"].Str() : std::string() ;
}
std::string File::Title() const
{
return m_title ;
}
std::string File::Filename() const
{
return m_filename ;
}
std::string File::Kind() const
{
return m_kind ;
}
std::string File::ServerMD5() const
{
return m_server_md5 ;
}
DateTime File::ServerModified() const
{
return m_server_modified ;
}
std::string File::Href() const
{
return m_href ;
}
std::string File::Parent() const
{
return m_parent ;
}
void File::Download( const Path& file, const http::Headers& auth ) const
{
http::GetFile( m_href, file.Str(), auth ) ;
os::SetFileTime( file, m_server_modified ) ;
}
void File::Upload( std::streambuf *file, const http::Headers& auth )
{
std::string meta =
"<?xml version='1.0' encoding='UTF-8'?>\n"
"<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:docs=\"http://schemas.google.com/docs/2007\">"
"<category scheme=\"http://schemas.google.com/g/2005#kind\" "
"term=\"http://schemas.google.com/docs/2007#file\"/>"
"<title>" + m_filename + "</title>"
"</entry>" ;
std::string data(
(std::istreambuf_iterator<char>(file)),
(std::istreambuf_iterator<char>()) ) ;
std::ostringstream xcontent_len ;
xcontent_len << "X-Upload-Content-Length: " << data.size() ;
http::Headers hdr( auth ) ;
hdr.push_back( "Content-Type: application/atom+xml" ) ;
hdr.push_back( "X-Upload-Content-Type: application/octet-stream" ) ;
hdr.push_back( xcontent_len.str() ) ;
hdr.push_back( "If-Match: " + m_etag ) ;
hdr.push_back( "Expect:" ) ;
Http http ;
http.Put( m_upload_link, meta, hdr ) ;
// parse the header and find "Location"
http::Headers uphdr ;
uphdr.push_back( "Expect:" ) ;
uphdr.push_back( "Accept:" ) ;
http.Put( http.RedirLocation(), data, uphdr ) ;
}
} // end of namespace

View File

@ -0,0 +1,39 @@
/*
Common URIs for REST API
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <string>
namespace gr { namespace v2 {
const std::string upload_base = "https://www.googleapis.com/upload/drive/v2/files" ;
namespace feeds
{
const std::string files = "https://www.googleapis.com/drive/v2/files" ;
const std::string changes = "https://www.googleapis.com/drive/v2/changes" ;
}
namespace mime_types
{
const std::string folder = "application/vnd.google-apps.folder" ;
}
} } // end of namespace gr::v2

View File

@ -0,0 +1,96 @@
/*
REST API item class implementation
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Entry2.hh"
#include "CommonUri.hh"
#include "util/Crypt.hh"
#include "util/log/Log.hh"
#include "util/OS.hh"
#include "json/Val.hh"
#include <algorithm>
#include <iterator>
namespace gr { namespace v2 {
/// construct an entry for remote, from "file" JSON object - Drive REST API
Entry2::Entry2( const Val& item )
{
Update( item ) ;
}
void Entry2::Update( const Val& item )
{
bool is_chg = item["kind"].Str() == "drive#change";
// changestamp only appears in change feed entries
m_change_stamp = is_chg ? item["id"].Int() : -1 ;
m_is_removed = is_chg && item["deleted"].Bool() ;
m_size = 0 ;
const Val& file = is_chg && !m_is_removed ? item["file"] : item;
if ( m_is_removed )
{
m_resource_id = item["fileId"];
}
else
{
m_title = file["title"] ;
m_etag = file["etag"] ;
Val fn;
m_filename = file.Get( "title", fn ) ? fn.Str() : std::string();
m_self_href = file["selfLink"] ;
m_mtime = DateTime( file["modifiedDate"] ) ;
m_resource_id = file["id"];
m_is_dir = file["mimeType"].Str() == mime_types::folder ;
m_is_editable = file["editable"].Bool() ;
m_is_removed = file["labels"]["trashed"].Bool() ;
if ( !m_is_dir )
{
if ( !file.Has( "md5Checksum" ) || !file.Has("downloadUrl") )
{
// This is either a google docs document or a not-yet-uploaded file. Ignore it.
// FIXME: We'll need to compare timestamps to support google docs.
m_is_removed = true;
}
else
{
m_md5 = file["md5Checksum"] ;
m_size = file["fileSize"].U64() ;
m_content_src = file["downloadUrl"] ;
// convert to lower case for easy comparison
std::transform( m_md5.begin(), m_md5.end(), m_md5.begin(), tolower ) ;
}
}
m_parent_hrefs.clear( ) ;
Val::Array parents = file["parents"].AsArray() ;
for ( Val::Array::iterator i = parents.begin() ; i != parents.end() ; ++i )
{
m_parent_hrefs.push_back( (*i)["isRoot"].Bool() ? std::string( "root" ) : (*i)["parentLink"] ) ;
}
}
}
} } // end of namespace gr::v2

View File

@ -0,0 +1,38 @@
/*
REST API item class implementation
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "base/Entry.hh"
namespace gr {
class Val ;
namespace v2 {
class Entry2: public Entry
{
public :
explicit Entry2( const Val& item ) ;
private :
void Update( const Val& item ) ;
} ;
} } // end of namespace gr::v2

View File

@ -0,0 +1,62 @@
/*
REST API item list ("Feed") implementation
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "CommonUri.hh"
#include "Feed2.hh"
#include "Entry2.hh"
#include "http/Agent.hh"
#include "http/Header.hh"
#include "json/Val.hh"
#include "json/ValResponse.hh"
#include <iostream>
#include <boost/format.hpp>
namespace gr { namespace v2 {
Feed2::Feed2( const std::string& url ):
Feed( url )
{
}
Feed2::~Feed2()
{
}
bool Feed2::GetNext( http::Agent *http )
{
if ( m_next.empty() )
return false ;
http::ValResponse out ;
http->Get( m_next, &out, http::Header(), 0 ) ;
Val m_content = out.Response() ;
Val::Array items = m_content["items"].AsArray() ;
m_entries.clear() ;
for ( Val::Array::iterator i = items.begin() ; i != items.end() ; ++i )
m_entries.push_back( Entry2( *i ) );
Val url ;
m_next = m_content.Get( "nextLink", url ) ? url : std::string( "" ) ;
return true ;
}
} } // end of namespace gr::v2

View File

@ -0,0 +1,38 @@
/*
REST API item list ("Feed") implementation
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "base/Feed.hh"
#include "util/Exception.hh"
#include <string>
namespace gr { namespace v2 {
class Feed2: public Feed
{
public :
Feed2( const std::string& url ) ;
~Feed2() ;
bool GetNext( http::Agent *http ) ;
} ;
} } // end of namespace gr::v2

View File

@ -0,0 +1,243 @@
/*
REST API Syncer implementation
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "base/Resource.hh"
#include "CommonUri.hh"
#include "Entry2.hh"
#include "Feed2.hh"
#include "Syncer2.hh"
#include "http/Agent.hh"
#include "http/Download.hh"
#include "http/Header.hh"
#include "http/StringResponse.hh"
#include "json/ValResponse.hh"
#include "json/JsonWriter.hh"
#include "util/OS.hh"
#include "util/log/Log.hh"
#include "util/StringStream.hh"
#include "util/ConcatStream.hh"
#include <boost/exception/all.hpp>
#include <cassert>
// for debugging
#include <iostream>
namespace gr { namespace v2 {
Syncer2::Syncer2( http::Agent *http ):
Syncer( http )
{
assert( http != 0 ) ;
}
void Syncer2::DeleteRemote( Resource *res )
{
http::StringResponse str ;
http::Header hdr ;
hdr.Add( "If-Match: " + res->ETag() ) ;
m_http->Post( res->SelfHref() + "/trash", "", &str, hdr ) ;
}
bool Syncer2::EditContent( Resource *res, bool new_rev )
{
assert( res->Parent() ) ;
assert( !res->ResourceID().empty() ) ;
assert( res->Parent()->GetState() == Resource::sync ) ;
if ( !res->IsEditable() )
{
Log( "Cannot upload %1%: file read-only. %2%", res->Name(), res->StateStr(), log::warning ) ;
return false ;
}
return Upload( res, new_rev ) ;
}
bool Syncer2::Create( Resource *res )
{
assert( res->Parent() ) ;
assert( res->Parent()->IsFolder() ) ;
assert( res->Parent()->GetState() == Resource::sync ) ;
assert( res->ResourceID().empty() ) ;
if ( !res->Parent()->IsEditable() )
{
Log( "Cannot upload %1%: parent directory read-only. %2%", res->Name(), res->StateStr(), log::warning ) ;
return false ;
}
return Upload( res, false );
}
bool Syncer2::Move( Resource* res, Resource* newParentRes, std::string newFilename )
{
if ( res->ResourceID().empty() )
{
Log("Can't rename file %1%, no server id found", res->Name());
return false;
}
Val meta;
meta.Add( "title", Val(newFilename) );
if ( res->IsFolder() )
{
meta.Add( "mimeType", Val( mime_types::folder ) );
}
std::string json_meta = WriteJson( meta );
Val valr ;
// Issue metadata update request
{
std::string addRemoveParents("");
if (res->Parent()->IsRoot() )
addRemoveParents += "&removeParents=root";
else
addRemoveParents += "&removeParents=" + res->Parent()->ResourceID();
if ( newParentRes->IsRoot() )
addRemoveParents += "&addParents=root";
else
addRemoveParents += "&addParents=" + newParentRes->ResourceID();
http::Header hdr2 ;
hdr2.Add( "Content-Type: application/json" );
http::ValResponse vrsp ;
// Don't change modified date because we're only moving
long http_code = m_http->Put(
feeds::files + "/" + res->ResourceID() + "?modifiedDateBehavior=noChange" + addRemoveParents,
json_meta, &vrsp, hdr2
) ;
valr = vrsp.Response();
assert( http_code == 200 && !( valr["id"].Str().empty() ) );
}
return true;
}
std::string to_string( uint64_t n )
{
std::ostringstream s;
s << n;
return s.str();
}
bool Syncer2::Upload( Resource *res, bool new_rev )
{
Val meta;
meta.Add( "title", Val( res->Name() ) );
if ( res->IsFolder() )
meta.Add( "mimeType", Val( mime_types::folder ) );
if ( !res->Parent()->IsRoot() )
{
Val parent;
parent.Add( "id", Val( res->Parent()->ResourceID() ) );
Val parents( Val::array_type );
parents.Add( parent );
meta.Add( "parents", parents );
}
std::string json_meta = WriteJson( meta );
Val valr ;
if ( res->IsFolder() )
{
// Only issue metadata update request
http::Header hdr2 ;
hdr2.Add( "Content-Type: application/json" );
http::ValResponse vrsp ;
long http_code = 0;
if ( res->ResourceID().empty() )
http_code = m_http->Post( feeds::files, json_meta, &vrsp, hdr2 ) ;
else
http_code = m_http->Put( feeds::files + "/" + res->ResourceID(), json_meta, &vrsp, hdr2 ) ;
valr = vrsp.Response();
assert( http_code == 200 && !( valr["id"].Str().empty() ) );
}
else
{
File file( res->Path() ) ;
uint64_t size = file.Size() ;
ConcatStream multipart ;
StringStream p1(
"--file_contents\r\nContent-Type: application/json; charset=utf-8\r\n\r\n" + json_meta +
"\r\n--file_contents\r\nContent-Type: application/octet-stream\r\nContent-Length: " + to_string( size ) +
"\r\n\r\n"
);
StringStream p2("\r\n--file_contents--\r\n");
multipart.Append( &p1 );
multipart.Append( &file );
multipart.Append( &p2 );
http::Header hdr ;
if ( !res->ETag().empty() )
hdr.Add( "If-Match: " + res->ETag() ) ;
hdr.Add( "Content-Type: multipart/related; boundary=\"file_contents\"" );
hdr.Add( "Content-Length: " + to_string( multipart.Size() ) );
http::ValResponse vrsp;
m_http->Request(
res->ResourceID().empty() ? "POST" : "PUT",
upload_base + ( res->ResourceID().empty() ? "" : "/" + res->ResourceID() ) +
"?uploadType=multipart&newRevision=" + ( new_rev ? "true" : "false" ),
&multipart, &vrsp, hdr
) ;
valr = vrsp.Response() ;
assert( !( valr["id"].Str().empty() ) );
}
Entry2 responseEntry = Entry2( valr ) ;
AssignIDs( res, responseEntry ) ;
res->SetServerTime( responseEntry.MTime() );
return true ;
}
std::unique_ptr<Feed> Syncer2::GetFolders()
{
return std::unique_ptr<Feed>( new Feed2( feeds::files + "?maxResults=100000&q=trashed%3dfalse+and+mimeType%3d%27" + mime_types::folder + "%27" ) );
}
std::unique_ptr<Feed> Syncer2::GetAll()
{
return std::unique_ptr<Feed>( new Feed2( feeds::files + "?maxResults=999999999&q=trashed%3dfalse" ) );
}
std::string ChangesFeed( long changestamp, int maxResults = 1000 )
{
boost::format feed( feeds::changes + "?maxResults=%1%&includeSubscribed=false" + ( changestamp > 0 ? "&startChangeId=%2%" : "" ) ) ;
return ( changestamp > 0 ? feed % maxResults % changestamp : feed % maxResults ).str() ;
}
std::unique_ptr<Feed> Syncer2::GetChanges( long min_cstamp )
{
return std::unique_ptr<Feed>( new Feed2( ChangesFeed( min_cstamp ) ) );
}
long Syncer2::GetChangeStamp( long min_cstamp )
{
http::ValResponse res ;
m_http->Get( ChangesFeed( min_cstamp, 1 ), &res, http::Header(), 0 ) ;
return std::atoi( res.Response()["largestChangeId"].Str().c_str() );
}
} } // end of namespace gr::v1

View File

@ -0,0 +1,53 @@
/*
REST API Syncer implementation
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "base/Syncer.hh"
namespace gr {
class Feed;
namespace v2 {
class Syncer2: public Syncer
{
public :
Syncer2( http::Agent *http );
void DeleteRemote( Resource *res );
bool EditContent( Resource *res, bool new_rev );
bool Create( Resource *res );
bool Move( Resource* res, Resource* newParent, std::string newFilename );
std::unique_ptr<Feed> GetFolders();
std::unique_ptr<Feed> GetAll();
std::unique_ptr<Feed> GetChanges( long min_cstamp );
long GetChangeStamp( long min_cstamp );
private :
bool Upload( Resource *res, bool new_rev );
} ;
} } // end of namespace gr::v2

View File

@ -0,0 +1,83 @@
/*
Convenience wrapper methods for various kinds of HTTP requests
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Agent.hh"
#include "Header.hh"
#include "util/StringStream.hh"
namespace gr {
namespace http {
Agent::Agent()
{
mMaxUpload = mMaxDownload = 0;
}
long Agent::Put(
const std::string& url,
const std::string& data,
DataStream *dest,
const Header& hdr )
{
StringStream s( data );
return Request( "PUT", url, &s, dest, hdr );
}
long Agent::Put(
const std::string& url,
File *file,
DataStream *dest,
const Header& hdr )
{
return Request( "PUT", url, (SeekStream*)file, dest, hdr );
}
long Agent::Get(
const std::string& url,
DataStream *dest,
const Header& hdr,
u64_t downloadFileBytes )
{
return Request( "GET", url, NULL, dest, hdr, downloadFileBytes );
}
long Agent::Post(
const std::string& url,
const std::string& data,
DataStream *dest,
const Header& hdr )
{
Header h( hdr ) ;
StringStream s( data );
h.Add( "Content-Type: application/x-www-form-urlencoded" );
return Request( "POST", url, &s, dest, h );
}
void Agent::SetUploadSpeed( unsigned kbytes )
{
mMaxUpload = kbytes;
}
void Agent::SetDownloadSpeed( unsigned kbytes )
{
mMaxDownload = kbytes;
}
} } // end of namespace

View File

@ -0,0 +1,94 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <string>
#include "ResponseLog.hh"
#include "util/Types.hh"
#include "util/Progress.hh"
namespace gr {
class SeekStream ;
class File ;
namespace http {
class Header ;
class Agent
{
protected:
unsigned mMaxUpload, mMaxDownload ;
public :
Agent() ;
virtual ~Agent() {}
virtual ResponseLog* GetLog() const = 0 ;
virtual void SetLog( ResponseLog* ) = 0 ;
virtual long Put(
const std::string& url,
const std::string& data,
DataStream *dest,
const Header& hdr ) ;
virtual long Put(
const std::string& url,
File *file,
DataStream *dest,
const Header& hdr ) ;
virtual long Get(
const std::string& url,
DataStream *dest,
const Header& hdr,
u64_t downloadFileBytes = 0 ) ;
virtual long Post(
const std::string& url,
const std::string& data,
DataStream *dest,
const Header& hdr ) ;
virtual long Request(
const std::string& method,
const std::string& url,
SeekStream *in,
DataStream *dest,
const Header& hdr,
u64_t downloadFileBytes = 0 ) = 0 ;
virtual void SetUploadSpeed( unsigned kbytes ) ;
virtual void SetDownloadSpeed( unsigned kbytes ) ;
virtual std::string LastError() const = 0 ;
virtual std::string LastErrorHeaders() const = 0 ;
virtual std::string RedirLocation() const = 0 ;
virtual std::string Escape( const std::string& str ) = 0 ;
virtual std::string Unescape( const std::string& str ) = 0 ;
virtual void SetProgressReporter( Progress* ) = 0;
} ;
} } // end of namespace

View File

@ -0,0 +1,303 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "CurlAgent.hh"
#include "Error.hh"
#include "Header.hh"
#include "util/log/Log.hh"
#include "util/DataStream.hh"
#include "util/File.hh"
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <limits>
#include <streambuf>
#include <iostream>
#include <signal.h>
namespace {
using namespace gr::http ;
using namespace gr ;
std::size_t ReadFileCallback( void *ptr, std::size_t size, std::size_t nmemb, SeekStream *file )
{
assert( ptr != 0 ) ;
assert( file != 0 ) ;
if ( size*nmemb > 0 )
return file->Read( static_cast<char*>(ptr), size*nmemb ) ;
return 0 ;
}
} // end of local namespace
namespace gr { namespace http {
struct CurlAgent::Impl
{
CURL *curl ;
std::string location ;
bool error ;
std::string error_headers ;
std::string error_data ;
DataStream *dest ;
u64_t total_download, total_upload ;
} ;
static struct curl_slist* SetHeader( CURL* handle, const Header& hdr );
CurlAgent::CurlAgent() : Agent(),
m_pimpl( new Impl ), m_pb( 0 )
{
m_pimpl->curl = ::curl_easy_init();
}
void CurlAgent::Init()
{
::curl_easy_reset( m_pimpl->curl ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_SSL_VERIFYPEER, 0L ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_SSL_VERIFYHOST, 0L ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADERFUNCTION, &CurlAgent::HeaderCallback ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADERDATA, this ) ;
::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADER, 0L ) ;
if ( mMaxUpload > 0 )
::curl_easy_setopt( m_pimpl->curl, CURLOPT_MAX_SEND_SPEED_LARGE, mMaxUpload ) ;
if ( mMaxDownload > 0 )
::curl_easy_setopt( m_pimpl->curl, CURLOPT_MAX_RECV_SPEED_LARGE, mMaxDownload ) ;
m_pimpl->error = false;
m_pimpl->error_headers = "";
m_pimpl->error_data = "";
m_pimpl->dest = NULL;
m_pimpl->total_download = m_pimpl->total_upload = 0;
}
CurlAgent::~CurlAgent()
{
::curl_easy_cleanup( m_pimpl->curl );
}
ResponseLog* CurlAgent::GetLog() const
{
return m_log.get();
}
void CurlAgent::SetLog(ResponseLog *log)
{
m_log.reset( log );
}
void CurlAgent::SetProgressReporter(Progress *progress)
{
m_pb = progress;
}
std::size_t CurlAgent::HeaderCallback( void *ptr, size_t size, size_t nmemb, CurlAgent *pthis )
{
char *str = static_cast<char*>(ptr) ;
std::string line( str, str + size*nmemb ) ;
// Check for error (HTTP 400 and above)
if ( line.substr( 0, 5 ) == "HTTP/" && line[9] >= '4' )
pthis->m_pimpl->error = true;
if ( pthis->m_pimpl->error )
pthis->m_pimpl->error_headers += line;
if ( pthis->m_log.get() )
pthis->m_log->Write( str, size*nmemb );
static const std::string loc = "Location: " ;
std::size_t pos = line.find( loc ) ;
if ( pos != line.npos )
{
std::size_t end_pos = line.find( "\r\n", pos ) ;
pthis->m_pimpl->location = line.substr( pos+loc.size(), end_pos - loc.size() ) ;
}
return size*nmemb ;
}
std::size_t CurlAgent::Receive( void* ptr, size_t size, size_t nmemb, CurlAgent *pthis )
{
assert( pthis != 0 ) ;
if ( pthis->m_log.get() )
pthis->m_log->Write( (const char*)ptr, size*nmemb );
if ( pthis->m_pimpl->error && pthis->m_pimpl->error_data.size() < 65536 )
{
// Do not feed error responses to destination stream
pthis->m_pimpl->error_data.append( static_cast<char*>(ptr), size * nmemb ) ;
return size * nmemb ;
}
return pthis->m_pimpl->dest->Write( static_cast<char*>(ptr), size * nmemb ) ;
}
int CurlAgent::progress_callback( CurlAgent *pthis, curl_off_t totalDownload, curl_off_t finishedDownload, curl_off_t totalUpload, curl_off_t finishedUpload )
{
// Only report download progress when set explicitly
if ( pthis->m_pb )
{
totalDownload = pthis->m_pimpl->total_download;
if ( !totalUpload )
totalUpload = pthis->m_pimpl->total_upload;
pthis->m_pb->reportProgress(
totalDownload > 0 ? totalDownload : totalUpload,
totalDownload > 0 ? finishedDownload : finishedUpload
);
}
return 0;
}
long CurlAgent::ExecCurl(
const std::string& url,
DataStream *dest,
const http::Header& hdr )
{
CURL *curl = m_pimpl->curl ;
assert( curl != 0 ) ;
char error[CURL_ERROR_SIZE] = {} ;
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error ) ;
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlAgent::Receive ) ;
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, this ) ;
m_pimpl->dest = dest ;
struct curl_slist *slist = SetHeader( m_pimpl->curl, hdr ) ;
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
#if LIBCURL_VERSION_NUM >= 0x072000
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this);
#endif
CURLcode curl_code = ::curl_easy_perform(curl);
curl_slist_free_all(slist);
// get the HTTP response code
long http_code = 0;
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
Trace( "HTTP response %1%", http_code ) ;
// reset the curl buffer to prevent it from touching our "error" buffer
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, 0 ) ;
m_pimpl->dest = NULL;
// only throw for libcurl errors
if ( curl_code != CURLE_OK )
{
BOOST_THROW_EXCEPTION(
Error()
<< CurlCode( curl_code )
<< Url( url )
<< CurlErrMsg( error )
<< HttpRequestHeaders( hdr )
) ;
}
return http_code ;
}
long CurlAgent::Request(
const std::string& method,
const std::string& url,
SeekStream *in,
DataStream *dest,
const Header& hdr,
u64_t downloadFileBytes )
{
Trace("HTTP %1% \"%2%\"", method, url ) ;
Init() ;
m_pimpl->total_download = downloadFileBytes ;
CURL *curl = m_pimpl->curl ;
// set common options
::curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str() );
if ( in )
{
::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ;
::curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadFileCallback ) ;
::curl_easy_setopt(curl, CURLOPT_READDATA , in ) ;
::curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, static_cast<curl_off_t>( in->Size() ) ) ;
}
return ExecCurl( url, dest, hdr ) ;
}
static struct curl_slist* SetHeader( CURL *handle, const Header& hdr )
{
// set headers
struct curl_slist *curl_hdr = 0 ;
for ( Header::iterator i = hdr.begin() ; i != hdr.end() ; ++i )
curl_hdr = curl_slist_append( curl_hdr, i->c_str() ) ;
::curl_easy_setopt( handle, CURLOPT_HTTPHEADER, curl_hdr ) ;
return curl_hdr;
}
std::string CurlAgent::LastError() const
{
return m_pimpl->error_data ;
}
std::string CurlAgent::LastErrorHeaders() const
{
return m_pimpl->error_headers ;
}
std::string CurlAgent::RedirLocation() const
{
return m_pimpl->location ;
}
std::string CurlAgent::Escape( const std::string& str )
{
CURL *curl = m_pimpl->curl ;
char *tmp = curl_easy_escape( curl, str.c_str(), str.size() ) ;
std::string result = tmp ;
curl_free( tmp ) ;
return result ;
}
std::string CurlAgent::Unescape( const std::string& str )
{
CURL *curl = m_pimpl->curl ;
int r ;
char *tmp = curl_easy_unescape( curl, str.c_str(), str.size(), &r ) ;
std::string result = tmp ;
curl_free( tmp ) ;
return result ;
}
} } // end of namespace

View File

@ -0,0 +1,86 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "Agent.hh"
#include <memory>
#include <string>
#include <curl/curl.h>
namespace gr {
class DataStream ;
namespace http {
/*! \brief agent to provide HTTP access
This class provides functions to send HTTP request in many methods (e.g. get, post and put).
Normally the HTTP response is returned in a Receivable.
*/
class CurlAgent : public Agent
{
public :
CurlAgent() ;
~CurlAgent() ;
ResponseLog* GetLog() const ;
void SetLog( ResponseLog *log ) ;
void SetProgressReporter( Progress *progress ) ;
long Request(
const std::string& method,
const std::string& url,
SeekStream *in,
DataStream *dest,
const Header& hdr,
u64_t downloadFileBytes = 0 ) ;
std::string LastError() const ;
std::string LastErrorHeaders() const ;
std::string RedirLocation() const ;
std::string Escape( const std::string& str ) ;
std::string Unescape( const std::string& str ) ;
static int progress_callback( CurlAgent *pthis, curl_off_t totalDownload, curl_off_t finishedDownload, curl_off_t totalUpload, curl_off_t finishedUpload );
private :
static std::size_t HeaderCallback( void *ptr, size_t size, size_t nmemb, CurlAgent *pthis ) ;
static std::size_t Receive( void* ptr, size_t size, size_t nmemb, CurlAgent *pthis ) ;
long ExecCurl(
const std::string& url,
DataStream *dest,
const Header& hdr ) ;
void Init() ;
private :
struct Impl ;
std::unique_ptr<Impl> m_pimpl ;
std::unique_ptr<ResponseLog> m_log ;
Progress* m_pb ;
} ;
} } // end of namespace

View File

@ -0,0 +1,83 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Download.hh"
// #include "util/SignalHandler.hh"
#include "util/Crypt.hh"
// boost headers
#include <boost/throw_exception.hpp>
#include <boost/exception/errinfo_api_function.hpp>
#include <boost/exception/errinfo_at_line.hpp>
#include <boost/exception/errinfo_errno.hpp>
#include <boost/exception/errinfo_file_handle.hpp>
#include <boost/exception/errinfo_file_name.hpp>
#include <boost/exception/errinfo_file_open_mode.hpp>
#include <boost/exception/info.hpp>
#include <cassert>
#include <new>
#include <signal.h>
namespace gr { namespace http {
Download::Download( const std::string& filename ) :
m_file( filename, 0600 ),
m_crypt( new crypt::MD5 )
{
}
Download::Download( const std::string& filename, NoChecksum ) :
m_file( filename, 0600 )
{
}
Download::~Download()
{
}
void Download::Clear()
{
// no need to do anything
}
std::string Download::Finish() const
{
return m_crypt.get() != 0 ? m_crypt->Get() : "" ;
}
std::size_t Download::Write( const char *data, std::size_t count )
{
assert( data != 0 ) ;
if ( m_crypt.get() != 0 )
m_crypt->Write( data, count ) ;
return m_file.Write( data, count ) ;
}
std::size_t Download::Read( char *data, std::size_t count )
{
return count ;
}
} } // end of namespace

View File

@ -19,28 +19,36 @@
#pragma once
#include <string>
#include <fstream>
#include "util/File.hh"
#include <openssl/evp.h>
#include <string>
namespace gr {
class Download
namespace crypt
{
class MD5 ;
}
namespace http {
class Download : public DataStream
{
public :
struct NoChecksum {} ;
Download( const std::string& filename ) ;
Download( const std::string& filename, NoChecksum ) ;
~Download( ) ;
~Download() ;
std::string Finish() const ;
static std::size_t Callback( char *data, std::size_t size, std::size_t nmemb, Download *pthis ) ;
void Clear() ;
std::size_t Write( const char *data, std::size_t count ) ;
std::size_t Read( char *, std::size_t ) ;
private :
std::ofstream m_file ;
EVP_MD_CTX *m_mdctx ;
File m_file ;
std::unique_ptr<crypt::MD5> m_crypt ;
} ;
} // end of namespace
} } // end of namespace

View File

@ -0,0 +1,50 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "Header.hh"
#include "util/Exception.hh"
namespace gr { namespace http {
struct Error : virtual Exception {} ;
// CURL error code
typedef boost::error_info<struct CurlCodeTag, int> CurlCode ;
// CURL error message
typedef boost::error_info<struct CurlErrMsgTag, std::string> CurlErrMsg ;
// URL
typedef boost::error_info<struct UrlTag, std::string> Url ;
// HTTP request headers
typedef boost::error_info<struct RequestHeadersTag, Header> HttpRequestHeaders ;
// HTTP response code
typedef boost::error_info<struct ResponseCodeTag, int> HttpResponseCode ;
// HTTP response headers
typedef boost::error_info<struct ResponseHeadersTag, std::string> HttpResponseHeaders ;
// HTTP response body
typedef boost::error_info<struct ResponseBodyTag, std::string> HttpResponseText ;
} } // end of namespace

View File

@ -0,0 +1,68 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Header.hh"
#include <algorithm>
#include <iterator>
#include <ostream>
#include <sstream>
namespace gr { namespace http {
Header::Header()
{
}
void Header::Add( const std::string& str )
{
m_vec.push_back( str ) ;
}
std::string Header::Str() const
{
std::ostringstream s ;
s << *this ;
return s.str() ;
}
Header::iterator Header::begin() const
{
return m_vec.begin() ;
}
Header::iterator Header::end() const
{
return m_vec.end() ;
}
std::ostream& operator<<( std::ostream& os, const Header& h )
{
std::copy( h.begin(), h.end(), std::ostream_iterator<std::string>( os, "\n" ) ) ;
return os ;
}
Header operator+( const Header& header, const std::string& str )
{
Header h( header ) ;
h.Add( str ) ;
return h ;
}
} } // end of namespace

View File

@ -0,0 +1,52 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <iosfwd>
#include <string>
#include <vector>
namespace gr { namespace http {
class Header
{
private :
typedef std::vector<std::string> Vec ;
public :
typedef Vec::const_iterator iterator ;
public :
Header() ;
void Add( const std::string& str ) ;
std::string Str() const ;
iterator begin() const ;
iterator end() const ;
private :
Vec m_vec ;
} ;
std::ostream& operator<<( std::ostream& os, const Header& h ) ;
Header operator+( const Header& header, const std::string& str ) ;
}} // end of namespace

View File

@ -0,0 +1,82 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ResponseLog.hh"
#include "util/log/Log.hh"
#include "util/DateTime.hh"
#include <cassert>
namespace gr { namespace http {
ResponseLog::ResponseLog(
const std::string& prefix,
const std::string& suffix )
{
Reset( prefix, suffix ) ;
}
std::size_t ResponseLog::Write( const char *data, std::size_t count )
{
if ( m_enabled )
{
assert( m_log.rdbuf() != 0 ) ;
m_log.rdbuf()->sputn( data, count ) ;
m_log.flush();
}
return count;
}
std::size_t ResponseLog::Read( char *data, std::size_t count )
{
return 0 ;
}
std::string ResponseLog::Filename( const std::string& prefix, const std::string& suffix )
{
return prefix + DateTime::Now().Format( "%F.%H%M%S" ) + suffix ;
}
void ResponseLog::Reset( const std::string& prefix, const std::string& suffix )
{
if ( m_log.is_open() )
m_log.close() ;
const std::string fname = Filename( prefix, suffix ) ;
// reset previous stream state. don't care if file can be opened
// successfully previously
m_log.clear() ;
// re-open the file
m_log.open( fname.c_str() ) ;
if ( m_log )
{
Trace( "logging HTTP response: %1%", fname ) ;
m_enabled = true ;
}
else
{
Trace( "cannot open log file %1%", fname ) ;
m_enabled = false ;
}
}
}} // end of namespace

View File

@ -0,0 +1,49 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/DataStream.hh"
#include <fstream>
#include <string>
namespace gr { namespace http {
class ResponseLog : public DataStream
{
public :
ResponseLog(
const std::string& prefix,
const std::string& suffix ) ;
std::size_t Write( const char *data, std::size_t count ) ;
std::size_t Read( char *data, std::size_t count ) ;
void Reset( const std::string& prefix, const std::string& suffix ) ;
private :
static std::string Filename( const std::string& prefix, const std::string& suffix ) ;
private :
bool m_enabled ;
std::ofstream m_log ;
} ;
} } // end of namespace

View File

@ -0,0 +1,49 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "StringResponse.hh"
namespace gr { namespace http {
StringResponse::StringResponse()
{
}
void StringResponse::Clear()
{
m_resp.clear() ;
}
std::size_t StringResponse::Write( const char *data, std::size_t count )
{
m_resp.append( data, count ) ;
return count ;
}
std::size_t StringResponse::Read( char *data, std::size_t count )
{
return count ;
}
const std::string& StringResponse::Response() const
{
return m_resp ;
}
} } // end of namespace

View File

@ -0,0 +1,43 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/DataStream.hh"
#include <string>
namespace gr { namespace http {
class StringResponse : public DataStream
{
public :
StringResponse() ;
std::size_t Write( const char *data, std::size_t count ) ;
std::size_t Read( char *data, std::size_t count ) ;
void Clear() ;
const std::string& Response() const ;
private :
std::string m_resp ;
} ;
} } // end of namespace

View File

@ -0,0 +1,58 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "XmlResponse.hh"
#include "xml/Node.hh"
#include "xml/TreeBuilder.hh"
namespace gr { namespace http {
XmlResponse::XmlResponse() : m_tb( new xml::TreeBuilder )
{
}
void XmlResponse::Clear()
{
m_tb.reset(new xml::TreeBuilder);
}
std::size_t XmlResponse::Write( const char *data, std::size_t count )
{
m_tb->ParseData( data, count ) ;
return count ;
}
std::size_t XmlResponse::Read( char *, std::size_t )
{
// throw something better
throw -1 ;
}
void XmlResponse::Finish()
{
m_tb->ParseData( 0, 0, true ) ;
}
xml::Node XmlResponse::Response() const
{
return m_tb->Result() ;
}
} } // end of namespace

View File

@ -0,0 +1,45 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "util/DataStream.hh"
#include "xml/TreeBuilder.hh"
#include <memory>
namespace gr { namespace http {
class XmlResponse : public DataStream
{
public :
XmlResponse() ;
void Clear() ;
std::size_t Write( const char *data, std::size_t count ) ;
std::size_t Read( char *data, std::size_t count ) ;
void Finish() ;
xml::Node Response() const ;
private :
std::unique_ptr<xml::TreeBuilder> m_tb ;
} ;
} } // end of namespace

View File

@ -0,0 +1,196 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include "JsonParser.hh"
#include "Val.hh"
#include "ValBuilder.hh"
#include <yajl/yajl_parse.h>
namespace gr {
namespace
{
int OnNull( void *ctx )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->VisitNull() ;
return true ;
}
int OnBool( void *ctx, int value )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->Visit( static_cast<bool>(value) ) ;
return true ;
}
int OnInt( void *ctx, long long value )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->Visit(value) ;
return true ;
}
int OnDouble( void *ctx, double value )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->Visit(value) ;
return true ;
}
int OnStr( void *ctx, const unsigned char *str, std::size_t len )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->Visit( std::string(reinterpret_cast<const char*>(str), len) ) ;
return true ;
}
int StartMap( void *ctx )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->StartObject() ;
return true ;
}
int OnMapKey( void *ctx, const unsigned char *str, std::size_t len )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->VisitKey( std::string(reinterpret_cast<const char*>(str), len) ) ;
return true ;
}
int EndMap( void *ctx )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->EndObject() ;
return true ;
}
int StartArray( void *ctx )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->StartArray() ;
return true ;
}
int EndArray( void *ctx )
{
ValVisitor *b = reinterpret_cast<ValVisitor*>(ctx) ;
b->EndArray() ;
return true ;
}
const yajl_callbacks callbacks = {
OnNull,
OnBool,
OnInt,
OnDouble,
0,
OnStr,
StartMap,
OnMapKey,
EndMap,
StartArray,
EndArray,
};
}
Val ParseJson( const std::string& json )
{
ValBuilder b;
JsonParser parser( &b ) ;
parser.Parse( json.c_str(), json.size() ) ;
parser.Finish() ;
return b.Result();
}
Val ParseJson( DataStream &in )
{
ValBuilder b;
JsonParser parser( &b ) ;
parser.Parse( in ) ;
parser.Finish() ;
return b.Result();
}
struct JsonParser::Impl
{
ValVisitor *callback ;
yajl_handle hand ;
} ;
JsonParser::JsonParser( ValVisitor *callback ) :
m_impl( new Impl )
{
m_impl->callback = callback ;
m_impl->hand = yajl_alloc( &callbacks, 0, m_impl->callback ) ;
}
JsonParser::~JsonParser()
{
yajl_free( m_impl->hand ) ;
}
void JsonParser::Parse( const char *str, std::size_t size )
{
const unsigned char *ustr = reinterpret_cast<unsigned const char*>(str) ;
yajl_status r = yajl_parse( m_impl->hand, ustr, size ) ;
if ( r != yajl_status_ok )
{
unsigned char *msg = yajl_get_error( m_impl->hand, true, ustr, size ) ;
std::string msg_str(reinterpret_cast<char*>(msg)) ;
yajl_free_error(m_impl->hand, msg) ;
BOOST_THROW_EXCEPTION(
Error()
<< ParseErr_(msg_str)
<< JsonText_(std::string(str,size))
);
}
}
void JsonParser::Parse( DataStream &in )
{
char buf[1024] ;
std::size_t count = 0 ;
while ( (count = in.Read( buf, sizeof(buf) ) ) > 0 )
{
Parse( buf, count );
}
}
void JsonParser::Finish()
{
if ( yajl_complete_parse(m_impl->hand) != yajl_status_ok )
{
unsigned char *msg = yajl_get_error( m_impl->hand, false, 0, 0 ) ;
std::string msg_str(reinterpret_cast<char*>(msg)) ;
yajl_free_error(m_impl->hand, msg) ;
BOOST_THROW_EXCEPTION( Error() << ParseErr_(msg_str) ) ;
}
}
} // end of namespace gr::json

View File

@ -0,0 +1,57 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#pragma once
#include "Val.hh"
#include "util/Exception.hh"
#include "util/DataStream.hh"
#include <string>
#include <memory>
namespace gr {
class ValVisitor ;
Val ParseJson( const std::string& json ) ;
Val ParseJson( DataStream &in ) ;
class JsonParser
{
public :
struct Error : virtual Exception {} ;
typedef boost::error_info<struct ParseErr, std::string> ParseErr_ ;
typedef boost::error_info<struct JsonText, std::string> JsonText_ ;
explicit JsonParser( ValVisitor *callback ) ;
~JsonParser() ;
void Parse( const char *str, std::size_t size ) ;
void Parse( DataStream &in ) ;
void Finish() ;
private :
struct Impl ;
std::unique_ptr<Impl> m_impl ;
} ;
} // end of namespace

View File

@ -0,0 +1,119 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include "JsonWriter.hh"
#include "util/StringStream.hh"
#include <yajl/yajl_gen.h>
#include <cassert>
namespace gr {
struct JsonWriter::Impl
{
yajl_gen gen ;
DataStream *out ;
} ;
JsonWriter::JsonWriter( DataStream *out ) :
m_impl( new Impl )
{
assert( out != 0 ) ;
m_impl->out = out ;
m_impl->gen = yajl_gen_alloc(0) ;
yajl_gen_config( m_impl->gen, yajl_gen_print_callback, &JsonWriter::WriteCallback, this ) ;
}
JsonWriter::~JsonWriter()
{
yajl_gen_free( m_impl->gen ) ;
}
void JsonWriter::Visit( long long t )
{
yajl_gen_integer( m_impl->gen, t ) ;
}
void JsonWriter::Visit( double t )
{
yajl_gen_double( m_impl->gen, t ) ;
}
void JsonWriter::Visit( const std::string& t )
{
yajl_gen_string( m_impl->gen,
reinterpret_cast<const unsigned char*>(t.c_str()), t.size() ) ;
}
void JsonWriter::Visit( bool t )
{
yajl_gen_bool( m_impl->gen, t ) ;
}
void JsonWriter::VisitNull()
{
yajl_gen_null( m_impl->gen ) ;
}
void JsonWriter::StartArray()
{
yajl_gen_array_open( m_impl->gen ) ;
}
void JsonWriter::EndArray()
{
yajl_gen_array_close( m_impl->gen ) ;
}
void JsonWriter::StartObject()
{
yajl_gen_map_open( m_impl->gen ) ;
}
void JsonWriter::VisitKey( const std::string& t )
{
Visit(t) ;
}
void JsonWriter::EndObject()
{
yajl_gen_map_close( m_impl->gen ) ;
}
void JsonWriter::WriteCallback( void *ctx, const char *str, std::size_t size )
{
JsonWriter *pthis = reinterpret_cast<JsonWriter*>(ctx) ;
assert( pthis != 0 ) ;
assert( pthis->m_impl->out != 0 ) ;
pthis->m_impl->out->Write( str, size ) ;
}
std::string WriteJson( const Val& val )
{
StringStream ss ;
JsonWriter wr( &ss ) ;
val.Visit( &wr ) ;
return ss.Str() ;
}
} // end of namespace

View File

@ -0,0 +1,60 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#pragma once
#include "Val.hh"
#include "ValVisitor.hh"
#include <memory>
namespace gr {
class DataStream ;
class JsonWriter : public ValVisitor
{
public :
JsonWriter( DataStream *out ) ;
~JsonWriter() ;
void Visit( long long t ) ;
void Visit( double t ) ;
void Visit( const std::string& t ) ;
void Visit( bool t ) ;
void VisitNull() ;
void StartArray() ;
void EndArray() ;
void StartObject() ;
void VisitKey( const std::string& t ) ;
void EndObject() ;
private :
static void WriteCallback( void *ctx, const char *str, std::size_t size ) ;
private :
struct Impl ;
std::unique_ptr<Impl> m_impl ;
} ;
std::string WriteJson( const Val& val );
} // end of namespace

315
libgrive/src/json/Val.cc Normal file
View File

@ -0,0 +1,315 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include "Val.hh"
#include "JsonWriter.hh"
#include "ValVisitor.hh"
#include "util/StdStream.hh"
#include <iostream>
namespace gr {
const Val& Val::Null()
{
static const Val null( null_type ) ;
return null ;
}
Val::Val( ) :
m_base( new Impl<Object> )
{
}
Val::Val( TypeEnum type )
{
switch ( type )
{
case int_type: m_base.reset( new Impl<long long> ) ; break ;
case bool_type: m_base.reset( new Impl<bool> ) ; break ;
case double_type: m_base.reset( new Impl<double> ) ; break ;
case string_type: m_base.reset( new Impl<std::string> ) ; break ;
case array_type: m_base.reset( new Impl<Array> ) ; break ;
case object_type: m_base.reset( new Impl<Object> ) ; break ;
case null_type:
default: m_base.reset( new Impl<void> ) ; break ;
}
}
Val::Val( const Val& v ) :
m_base( v.m_base->Clone() )
{
}
Val::~Val()
{
}
void Val::Swap( Val& val )
{
std::swap( m_base, val.m_base ) ;
}
Val& Val::operator=( const Val& val )
{
Val tmp(val) ;
Swap(tmp) ;
return *this ;
}
Val::TypeEnum Val::Type() const
{
return m_base->Type() ;
}
const Val& Val::operator[]( const std::string& key ) const
{
const Object& obj = As<Object>() ;
Object::const_iterator i = obj.find(key) ;
if ( i != obj.end() )
return i->second ;
// shut off compiler warning
BOOST_THROW_EXCEPTION(Error() << NoKey_(key)) ;
throw ;
}
Val& Val::operator[]( const std::string& key )
{
Object& obj = As<Object>() ;
Object::iterator i = obj.find(key) ;
if ( i != obj.end() )
return i->second ;
// shut off compiler warning
BOOST_THROW_EXCEPTION(Error() << NoKey_(key)) ;
throw ;
}
const Val& Val::operator[]( std::size_t index ) const
{
const Array& ar = As<Array>() ;
if ( index < ar.size() )
return ar[index] ;
// shut off compiler warning
BOOST_THROW_EXCEPTION(Error() << OutOfRange_(index)) ;
throw ;
}
std::string Val::Str() const
{
if ( Type() == int_type )
return boost::to_string( As<long long>() );
return As<std::string>() ;
}
Val::operator std::string() const
{
return Str();
}
int Val::Int() const
{
if ( Type() == string_type )
return std::atoi( As<std::string>().c_str() );
return static_cast<int>(As<long long>()) ;
}
unsigned long long Val::U64() const
{
if ( Type() == string_type )
return strtoull( As<std::string>().c_str(), NULL, 10 );
return static_cast<unsigned long long>(As<long long>()) ;
}
double Val::Double() const
{
if ( Type() == string_type )
return std::atof( As<std::string>().c_str() );
return As<double>() ;
}
bool Val::Bool() const
{
return As<bool>() ;
}
const Val::Array& Val::AsArray() const
{
return As<Array>() ;
}
Val::Array& Val::AsArray()
{
return As<Array>() ;
}
const Val::Object& Val::AsObject() const
{
return As<Object>() ;
}
Val::Object& Val::AsObject()
{
return As<Object>() ;
}
bool Val::Has( const std::string& key ) const
{
const Object& obj = As<Object>() ;
return obj.find(key) != obj.end() ;
}
bool Val::Del( const std::string& key )
{
Object& obj = As<Object>() ;
return obj.erase(key) > 0 ;
}
Val& Val::Item( const std::string& key )
{
return As<Object>()[key];
}
bool Val::Get( const std::string& key, Val& val ) const
{
const Object& obj = As<Object>() ;
Object::const_iterator i = obj.find(key) ;
if ( i != obj.end() )
{
val = i->second ;
return true ;
}
else
return false ;
}
void Val::Add( const std::string& key, const Val& value )
{
As<Object>().insert( std::make_pair(key, value) ) ;
}
void Val::Set( const std::string& key, const Val& value )
{
Object& obj = As<Object>();
Object::iterator i = obj.find(key);
if (i == obj.end())
obj.insert(std::make_pair(key, value));
else
i->second = value;
}
void Val::Add( const Val& json )
{
As<Array>().push_back( json ) ;
}
void Val::Visit( ValVisitor *visitor ) const
{
switch ( Type() )
{
case null_type: visitor->VisitNull() ; break ;
case int_type: visitor->Visit( As<long long>() ) ; break ;
case double_type: visitor->Visit( As<double>() ) ; break ;
case string_type: visitor->Visit( As<std::string>() ) ; break ;
case bool_type: visitor->Visit( As<bool>() ) ; break ;
case object_type:
{
visitor->StartObject() ;
const Object& obj = As<Object>() ;
for ( Object::const_iterator i = obj.begin() ; i != obj.end() ; ++i )
{
visitor->VisitKey( i->first ) ;
i->second.Visit( visitor ) ;
}
visitor->EndObject() ;
break ;
}
case array_type:
{
visitor->StartArray() ;
const Array& arr = As<Array>() ;
for ( Array::const_iterator i = arr.begin() ; i != arr.end() ; ++i )
i->Visit( visitor ) ;
visitor->EndArray() ;
break ;
}
}
}
void Val::Select( const Object& obj, const std::string& key, std::vector<Val>& result ) const
{
Object::const_iterator i = obj.find(key) ;
if ( i != obj.end() )
result.push_back(i->second) ;
}
/** If \a this is an array of objects, this function returns all values of
the objects in the array with the key \a key. If \a this is an object,
just return the value with the key \a key.
*/
std::vector<Val> Val::Select( const std::string& key ) const
{
std::vector<Val> result ;
if ( Is<Object>() )
Select( As<Object>(), key, result ) ;
else if ( Is<Array>() )
{
const Array& array = As<Array>() ;
for ( Array::const_iterator i = array.begin() ; i != array.end() ; ++i )
{
if ( i->Is<Object>() )
Select( i->As<Object>(), key, result ) ;
}
}
return result ;
}
std::ostream& operator<<( std::ostream& os, const Val& val )
{
StdStream ss( os.rdbuf() ) ;
JsonWriter wr( &ss ) ;
val.Visit( &wr ) ;
return os ;
}
} // end of namespace
namespace std
{
void swap( gr::Val& v1, gr::Val& v2 )
{
v1.Swap( v2 ) ;
}
ostream& operator<<( ostream& os, gr::Val::TypeEnum t )
{
return os << static_cast<int>(t) ;
}
}

234
libgrive/src/json/Val.hh Normal file
View File

@ -0,0 +1,234 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#pragma once
#include "util/Exception.hh"
#include <cstddef>
#include <iosfwd>
#include <map>
#include <memory>
#include <string>
#include <vector>
namespace gr {
class ValVisitor ;
class Val
{
public :
enum TypeEnum { null_type, bool_type, double_type, int_type, object_type, array_type, string_type } ;
struct Error : virtual Exception {} ;
typedef boost::error_info<struct SrcType, TypeEnum> SrcType_ ;
typedef boost::error_info<struct DestType, TypeEnum> DestType_ ;
typedef boost::error_info<struct NoKey, std::string> NoKey_ ;
typedef boost::error_info<struct OutOfRange,std::size_t> OutOfRange_ ;
private :
template <typename T>
struct Type2Enum ;
template <typename T>
struct SupportType ;
public :
typedef std::vector<Val> Array ;
typedef std::map<std::string, Val> Object ;
public :
Val() ;
Val( const Val& v ) ;
explicit Val( TypeEnum type ) ;
~Val() ;
static const Val& Null() ;
template <typename T>
explicit Val( const T& t )
{
Assign(t) ;
}
template <typename T>
Val& Assign( const T& t ) ;
void Swap( Val& val ) ;
Val& operator=( const Val& val ) ;
template <typename T>
Val& operator=( const T& t )
{
return Assign(t) ;
}
operator std::string() const ;
template <typename T>
const T& As() const ;
template <typename T>
T& As() ;
template <typename T>
bool Is() const ;
TypeEnum Type() const ;
// shortcuts for As<>()
std::string Str() const ;
int Int() const ;
unsigned long long U64() const ;
double Double() const ;
bool Bool() const ;
const Array& AsArray() const ;
Array& AsArray() ;
const Object& AsObject() const ;
Object& AsObject() ;
// shortcuts for objects
Val& operator[]( const std::string& key ) ; // get updatable ref or throw
const Val& operator[]( const std::string& key ) const ; // get const ref or throw
Val& Item( const std::string& key ) ; // insert if not exists and get
bool Has( const std::string& key ) const ; // check if exists
bool Get( const std::string& key, Val& val ) const ; // get or return false
void Add( const std::string& key, const Val& val ) ; // insert or do nothing
void Set( const std::string& key, const Val& val ) ; // insert or update
bool Del( const std::string& key ); // delete or do nothing
// shortcuts for array (and array of objects)
const Val& operator[]( std::size_t index ) const ;
void Add( const Val& json ) ;
std::vector<Val> Select( const std::string& key ) const ;
friend std::ostream& operator<<( std::ostream& os, const Val& val ) ;
void Visit( ValVisitor *visitor ) const ;
private :
struct Base ;
template <typename T>
struct Impl ;
std::unique_ptr<Base> m_base ;
private :
void Select( const Object& obj, const std::string& key, std::vector<Val>& result ) const ;
} ;
template <> struct Val::Type2Enum<void> { static const TypeEnum type = null_type ; } ;
template <> struct Val::Type2Enum<long long> { static const TypeEnum type = int_type ; } ;
template <> struct Val::Type2Enum<bool> { static const TypeEnum type = bool_type ; } ;
template <> struct Val::Type2Enum<double> { static const TypeEnum type = double_type ;} ;
template <> struct Val::Type2Enum<std::string> { static const TypeEnum type = string_type ; } ;
template <> struct Val::Type2Enum<Val::Array> { static const TypeEnum type = array_type ; } ;
template <> struct Val::Type2Enum<Val::Object> { static const TypeEnum type = object_type ; } ;
template <> struct Val::SupportType<int> { typedef long long Type ; } ;
template <> struct Val::SupportType<unsigned> { typedef long long Type ; } ;
template <> struct Val::SupportType<long> { typedef long long Type ; } ;
template <> struct Val::SupportType<unsigned long> { typedef long long Type ; } ;
template <> struct Val::SupportType<short> { typedef long long Type ; } ;
template <> struct Val::SupportType<unsigned short> { typedef long long Type ; } ;
template <> struct Val::SupportType<long long> { typedef long long Type ; } ;
template <> struct Val::SupportType<unsigned long long> { typedef long long Type ; } ;
template <> struct Val::SupportType<bool> { typedef bool Type ; } ;
template <> struct Val::SupportType<double> { typedef double Type ; } ;
template <> struct Val::SupportType<std::string> { typedef std::string Type ; } ;
template <> struct Val::SupportType<const char*> { typedef std::string Type ; } ;
template <> struct Val::SupportType<Val::Array> { typedef Val::Array Type ; } ;
template <> struct Val::SupportType<Val::Object> { typedef Val::Object Type ; } ;
struct Val::Base
{
virtual ~Base() {}
virtual Base* Clone() const = 0 ;
virtual TypeEnum Type() const = 0 ;
} ;
template <typename T>
struct Val::Impl : public Base
{
T val ;
Impl( ) : val() {}
Impl( const T& t ) : val(t) {}
Impl<T>* Clone() const { return new Impl<T>(val); }
TypeEnum Type() const { return Type2Enum<T>::type ; }
} ;
template <>
struct Val::Impl<void> : public Base
{
Impl<void>* Clone() const { return new Impl<void>; }
TypeEnum Type() const { return null_type ; }
} ;
template <typename T>
Val& Val::Assign( const T& t )
{
m_base.reset( new Impl<typename SupportType<T>::Type>(t) ) ;
return *this ;
}
template <typename T>
const T& Val::As() const
{
const Impl<T> *impl = dynamic_cast<const Impl<T> *>( m_base.get() ) ;
if ( !impl )
{
TypeEnum dest = Type2Enum<T>::type ;
BOOST_THROW_EXCEPTION(
Error() << SrcType_( Type() ) << DestType_( dest )
) ;
}
return impl->val ;
}
template <typename T>
T& Val::As()
{
Impl<T> *impl = dynamic_cast<Impl<T> *>( m_base.get() ) ;
if ( !impl )
{
TypeEnum dest = Type2Enum<T>::type ;
BOOST_THROW_EXCEPTION(
Error() << SrcType_( Type() ) << DestType_( dest )
) ;
}
return impl->val ;
}
template <typename T>
bool Val::Is() const
{
return Type() == Type2Enum<T>::type ;
}
} // end of namespace
namespace std
{
void swap( gr::Val& v1, gr::Val& v2 ) ;
ostream& operator<<( ostream& os, gr::Val::TypeEnum t ) ;
}

View File

@ -0,0 +1,140 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include "ValBuilder.hh"
namespace gr {
ValBuilder::ValBuilder( )
{
}
ValBuilder::~ValBuilder()
{
}
void ValBuilder::Visit( long long t )
{
Build(Val(t)) ;
}
void ValBuilder::Visit( double t )
{
Build(Val(t)) ;
}
void ValBuilder::Visit( const std::string& t )
{
Build(Val(t)) ;
}
void ValBuilder::Visit( bool t )
{
Build(Val(t)) ;
}
void ValBuilder::VisitNull()
{
Build(Val()) ;
}
void ValBuilder::Build( const Val& t )
{
if ( m_ctx.empty() )
{
Level l = { Val::Null(), t } ;
m_ctx.push( l ) ;
}
else if ( m_ctx.top().val.Is<Val::Array>() )
{
Val::Array& ar = m_ctx.top().val.As<Val::Array>() ;
ar.push_back( t ) ;
}
else if ( m_ctx.top().val.Is<Val::Object>() )
{
if ( !m_ctx.top().key.Is<std::string>() )
BOOST_THROW_EXCEPTION( Error() << NoKey_(t) ) ;
else
{
Val::Object& obj = m_ctx.top().val.As<Val::Object>() ;
obj.insert( std::make_pair( m_ctx.top().key.Str(), t ) ) ;
m_ctx.top().key = Val::Null() ;
}
}
else
BOOST_THROW_EXCEPTION( Error() << Unexpected_(m_ctx.top().val) ) ;
}
void ValBuilder::VisitKey( const std::string& t )
{
m_ctx.top().key = t ;
}
void ValBuilder::StartArray()
{
Level l = { Val::Null(), Val(Val::Array()) } ;
m_ctx.push(l) ;
}
void ValBuilder::EndArray()
{
End( Val::array_type ) ;
}
void ValBuilder::End( Val::TypeEnum type )
{
if ( m_ctx.top().val.Type() == type )
{
if( !m_ctx.top().key.Is<void>() )
BOOST_THROW_EXCEPTION( Error() << Unexpected_(m_ctx.top().key) ) ;
// get top Val from stack
Val current ;
current.Swap( m_ctx.top().val ) ;
m_ctx.pop() ;
Build(current) ;
}
}
void ValBuilder::StartObject()
{
Level l = { Val::Null(), Val( Val::Object() ) } ;
m_ctx.push(l) ;
}
void ValBuilder::EndObject()
{
End( Val::object_type ) ;
}
Val ValBuilder::Result() const
{
if ( !m_ctx.size() )
BOOST_THROW_EXCEPTION( Error() << NoKey_( Val(std::string("")) ) ) ;
Val r = m_ctx.top().val;
if ( m_ctx.size() > 1 )
BOOST_THROW_EXCEPTION( Error() << Unexpected_(m_ctx.top().val) ) ;
return r;
}
} // end of namespace

View File

@ -0,0 +1,74 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#pragma once
#include "ValVisitor.hh"
#include "Val.hh"
#include "util/Exception.hh"
#include <stack>
#include <memory>
namespace gr {
class ValBuilder : public ValVisitor
{
public :
struct Error : virtual Exception {} ;
typedef boost::error_info<struct Mismatch, Val> Mismatch_ ;
typedef boost::error_info<struct Unexpected, Val> Unexpected_ ;
typedef boost::error_info<struct NoKey, Val> NoKey_ ;
public :
ValBuilder( ) ;
~ValBuilder() ;
void Visit( long long t ) ;
void Visit( double t ) ;
void Visit( const std::string& t ) ;
void Visit( bool t ) ;
void VisitNull() ;
void Build( const Val& t ) ;
void StartArray() ;
void EndArray() ;
void StartObject() ;
void VisitKey( const std::string& t ) ;
void EndObject() ;
Val Result() const ;
private :
void End( Val::TypeEnum type ) ;
private :
struct Level
{
Val key ;
Val val ;
} ;
std::stack<Level> m_ctx ;
} ;
} // end of namespace

View File

@ -0,0 +1,53 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include "ValResponse.hh"
#include "Val.hh"
namespace gr { namespace http {
ValResponse::ValResponse( ) :
m_parser( &m_val )
{
}
std::size_t ValResponse::Write( const char *data, std::size_t count )
{
m_parser.Parse( data, count ) ;
return count ;
}
std::size_t ValResponse::Read( char *data, std::size_t count )
{
return count ;
}
Val ValResponse::Response() const
{
return m_val.Result() ;
}
void ValResponse::Finish()
{
m_parser.Finish() ;
}
} } // end of namespace gr::http

View File

@ -0,0 +1,52 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#pragma once
#include "util/DataStream.hh"
#include "JsonParser.hh"
#include "ValBuilder.hh"
namespace gr
{
class Val ;
}
namespace gr { namespace http {
class ValResponse : public DataStream
{
public :
ValResponse() ;
std::size_t Write( const char *data, std::size_t count ) ;
std::size_t Read( char *data, std::size_t count ) ;
void Finish() ;
Val Response() const ;
private :
ValBuilder m_val ;
JsonParser m_parser ;
} ;
} } // end of namespace gr::http

View File

@ -0,0 +1,46 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2013 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#pragma once
#include <string>
namespace gr {
class ValVisitor
{
public :
virtual ~ValVisitor() {}
virtual void Visit( long long t ) = 0 ;
virtual void Visit( double t ) = 0 ;
virtual void Visit( const std::string& t ) = 0 ;
virtual void Visit( bool t ) = 0 ;
virtual void VisitNull() = 0 ;
virtual void StartArray() = 0 ;
virtual void EndArray() = 0 ;
virtual void StartObject() = 0 ;
virtual void VisitKey( const std::string& t ) = 0 ;
virtual void EndObject() = 0 ;
} ;
} // end of namespace

View File

@ -1,33 +0,0 @@
# lib subproject
file(GLOB DRIVE_SOURCES
drive/*.cc
)
file(GLOB PROTOCOL_SOURCES
protocol/*.cc
)
file(GLOB UTIL_SOURCES
util/*.cc
)
file(GLOB DRIVE_HEADERS
drive/*.hh
)
file (GLOB PROTOCOL_HEADERS
protocol/*.hh
)
file (GLOB UTIL_HEADERS
util/*.hh
)
add_library( grive SHARED
${DRIVE_SOURCES}
${PROTOCOL_SOURCES}
${UTIL_SOURCES}
)
nstall(FILES ${UTIL_HEADERS} DESTINATION include/grive/util)

View File

@ -0,0 +1,176 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "AuthAgent.hh"
#include "http/Error.hh"
#include "http/Header.hh"
#include "util/log/Log.hh"
#include "util/OS.hh"
#include "util/File.hh"
#include <cassert>
namespace gr {
using namespace http ;
AuthAgent::AuthAgent( OAuth2& auth, Agent *real_agent ) :
Agent(),
m_auth ( auth ),
m_agent ( real_agent )
{
}
http::ResponseLog* AuthAgent::GetLog() const
{
return m_agent->GetLog();
}
void AuthAgent::SetLog( http::ResponseLog *log )
{
return m_agent->SetLog( log );
}
void AuthAgent::SetProgressReporter( Progress *progress )
{
m_agent->SetProgressReporter( progress );
}
void AuthAgent::SetUploadSpeed( unsigned kbytes )
{
m_agent->SetUploadSpeed( kbytes );
}
void AuthAgent::SetDownloadSpeed( unsigned kbytes )
{
m_agent->SetDownloadSpeed( kbytes );
}
http::Header AuthAgent::AppendHeader( const http::Header& hdr ) const
{
http::Header h(hdr) ;
h.Add( "Authorization: Bearer " + m_auth.AccessToken() ) ;
h.Add( "GData-Version: 3.0" ) ;
return h ;
}
long AuthAgent::Request(
const std::string& method,
const std::string& url,
SeekStream *in,
DataStream *dest,
const http::Header& hdr,
u64_t downloadFileBytes )
{
long response;
Header auth;
m_interval = 0;
do
{
auth = AppendHeader( hdr );
if ( in )
in->Seek( 0, 0 );
response = m_agent->Request( method, url, in, dest, auth, downloadFileBytes );
} while ( CheckRetry( response ) );
return CheckHttpResponse( response, url, auth );
}
std::string AuthAgent::LastError() const
{
return m_agent->LastError() ;
}
std::string AuthAgent::LastErrorHeaders() const
{
return m_agent->LastErrorHeaders() ;
}
std::string AuthAgent::RedirLocation() const
{
return m_agent->RedirLocation() ;
}
std::string AuthAgent::Escape( const std::string& str )
{
return m_agent->Escape( str ) ;
}
std::string AuthAgent::Unescape( const std::string& str )
{
return m_agent->Unescape( str ) ;
}
bool AuthAgent::CheckRetry( long response )
{
// HTTP 500 and 503 should be temporary. just wait a bit and retry
if ( response == 500 || response == 503 )
{
Log( "request failed due to temporary error: %1% (body: %2%). retrying in 5 seconds",
response, m_agent->LastError(), log::warning ) ;
os::Sleep( 5 ) ;
return true ;
}
// HTTP 403 is the result of API rate limiting. attempt exponential backoff and try again
else if ( response == 429 || ( response == 403 && (
m_agent->LastError().find("\"reason\": \"userRateLimitExceeded\",") != std::string::npos ||
m_agent->LastError().find("\"reason\": \"rateLimitExceeded\",") != std::string::npos ) ) )
{
m_interval = m_interval <= 0 ? 1 : ( m_interval < 64 ? m_interval*2 : 120 );
Log( "request failed due to rate limiting: %1% (body: %2%). retrying in %3% seconds",
response, m_agent->LastError(), m_interval, log::warning ) ;
os::Sleep( m_interval ) ;
return true ;
}
// HTTP 401 Unauthorized. the auth token has been expired. refresh it
else if ( response == 401 )
{
Log( "request failed due to auth token expired: %1% (body: %2%). refreshing token",
response, m_agent->LastError(), log::warning ) ;
os::Sleep( 5 ) ;
m_auth.Refresh() ;
return true ;
}
else
return false ;
}
long AuthAgent::CheckHttpResponse(
long response,
const std::string& url,
const http::Header& hdr )
{
// throw for other HTTP errors
if ( response >= 400 )
{
BOOST_THROW_EXCEPTION(
Error()
<< HttpResponseCode( response )
<< HttpResponseHeaders( m_agent->LastErrorHeaders() )
<< HttpResponseText( m_agent->LastError() )
<< Url( url )
<< HttpRequestHeaders( hdr ) ) ;
}
return response ;
}
} // end of namespace

View File

@ -0,0 +1,77 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "http/Agent.hh"
#include "OAuth2.hh"
#include <memory>
namespace gr {
/*! \brief An HTTP agent with support OAuth2
This is a HTTP agent that provide support for OAuth2. It will also perform retries on
certain HTTP errors.
*/
class AuthAgent : public http::Agent
{
public :
AuthAgent( OAuth2& auth, http::Agent* real_agent ) ;
http::ResponseLog* GetLog() const ;
void SetLog( http::ResponseLog *log ) ;
long Request(
const std::string& method,
const std::string& url,
SeekStream *in,
DataStream *dest,
const http::Header& hdr,
u64_t downloadFileBytes = 0 ) ;
std::string LastError() const ;
std::string LastErrorHeaders() const ;
std::string RedirLocation() const ;
std::string Escape( const std::string& str ) ;
std::string Unescape( const std::string& str ) ;
void SetUploadSpeed( unsigned kbytes ) ;
void SetDownloadSpeed( unsigned kbytes ) ;
void SetProgressReporter( Progress *progress ) ;
private :
http::Header AppendHeader( const http::Header& hdr ) const ;
bool CheckRetry( long response ) ;
long CheckHttpResponse(
long response,
const std::string& url,
const http::Header& hdr ) ;
private :
OAuth2& m_auth ;
http::Agent* m_agent ;
int m_interval ;
} ;
} // end of namespace

View File

@ -1,96 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Download.hh"
// #include "util/SignalHandler.hh"
#include <openssl/evp.h>
#include <cassert>
#include <new>
#include <stdexcept>
#include <signal.h>
namespace gr {
Download::Download( const std::string& filename ) :
m_file( filename.c_str(), std::ios::out | std::ios::binary ),
m_mdctx( ::EVP_MD_CTX_create() )
{
if ( m_mdctx == 0 )
throw std::bad_alloc() ;
if ( ::EVP_DigestInit_ex( m_mdctx, ::EVP_md5(), 0 ) != 1 )
throw std::runtime_error( "cannot create MD5 digest context" ) ;
if ( !m_file )
throw std::runtime_error( "cannot open file " + filename + " for writing" ) ;
}
Download::Download( const std::string& filename, NoChecksum ) :
m_file( filename.c_str(), std::ios::out | std::ios::binary ),
m_mdctx( 0 )
{
if ( !m_file )
throw std::runtime_error( "cannot open file " + filename + " for writing" ) ;
}
Download::~Download( )
{
if ( m_mdctx != 0 )
::EVP_MD_CTX_destroy( m_mdctx ) ;
}
std::string Download::Finish() const
{
// Unregister the signal
// SignalHandler::GetInstance().UnregisterSignal( SIGINT ) ;
std::string result ;
// get the checksum and return it ;
if ( m_mdctx != 0 )
{
unsigned int size = EVP_MAX_MD_SIZE ;
result.resize( size ) ;
if ( ::EVP_DigestFinal_ex( m_mdctx, reinterpret_cast<unsigned char*>(&result[0]), &size ) != 1 )
throw std::runtime_error( "cannot calculate checksum" ) ;
result.resize( size ) ;
}
return result ;
}
std::size_t Download::Callback( char *data, std::size_t size, std::size_t nmemb, Download *pthis )
{
assert( pthis != 0 ) ;
assert( data != 0 ) ;
std::size_t count = size * nmemb ;
if ( pthis->m_mdctx != 0 )
::EVP_DigestUpdate( pthis->m_mdctx, data, count ) ;
return pthis->m_file.rdbuf()->sputn( data, count ) ;
}
} // end of namespace

View File

@ -1,372 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "HTTP.hh"
#include "Download.hh"
// #include "util/SignalHandler.hh"
// dependent libraries
#include <curl/curl.h>
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
#include <sstream>
#include <streambuf>
#include <signal.h>
namespace {
using namespace gr::http ;
// libcurl callback to append to a string
std::size_t WriteCallback( char *data, size_t size, size_t nmemb, std::string *resp )
{
assert( resp != 0 ) ;
assert( data != 0 ) ;
std::size_t count = size * nmemb ;
resp->append( data, count ) ;
return count ;
}
size_t ReadCallback( void *ptr, std::size_t size, std::size_t nmemb, std::string *data )
{
assert( ptr != 0 ) ;
assert( data != 0 ) ;
std::size_t count = std::min( size * nmemb, data->size() ) ;
if ( count > 0 )
{
std::memcpy( ptr, &(*data)[0], count ) ;
data->erase( 0, count ) ;
}
return count ;
}
CURL* InitCurl( const std::string& url, std::string *resp, const Headers& hdr )
{
CURL *curl = curl_easy_init();
if ( curl == 0 )
throw std::bad_alloc() ;
// set common options
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp ) ;
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// set headers
struct curl_slist *curl_hdr = 0 ;
for ( Headers::const_iterator i = hdr.begin() ; i != hdr.end() ; ++i )
curl_hdr = curl_slist_append( curl_hdr, i->c_str() ) ;
curl_easy_setopt( curl, CURLOPT_HTTPHEADER, curl_hdr ) ;
return curl ;
}
void DoCurl( CURL *curl )
{
char error_buf[CURL_ERROR_SIZE] = {} ;
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf ) ;
CURLcode curl_code = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
// clean up
curl_easy_cleanup(curl);
if ( curl_code != CURLE_OK )
{
throw Exception( curl_code, http_code, error_buf ) ;
}
else if (http_code >= 400 )
{
std::cout << "http error " << http_code << std::endl ;
throw Exception( curl_code, http_code, error_buf ) ;
}
}
// Callback for SIGINT
void CallbackInt( int )
{
// TODO: instead of just disabling the signal, clean up the environment
// and exit gracefully
std::cout << " Signal disabled while downloading file..\n";
}
} // end of local namespace
namespace gr { namespace http {
Exception::Exception( int curl_code, int http_code, const char *err_buf )
: runtime_error( Format( curl_code, http_code, err_buf ) )
{
}
std::string Exception::Format( int curl_code, int http_code, const char *err_buf )
{
std::ostringstream ss ;
ss << "CURL code = " << curl_code << " HTTP code = " << http_code << " (" << err_buf << ")" ;
return ss.str() ;
}
std::string Get( const std::string& url, const Headers& hdr )
{
std::string resp ;
CURL *curl = InitCurl( url, &resp, hdr ) ;
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
DoCurl( curl ) ;
return resp;
}
void GetFile(
const std::string& url,
const std::string& filename,
const Headers& hdr )
{
// Register the callback
// SignalHandler::GetInstance().RegisterSignal( SIGINT, &CallbackInt ) ;
Download dl( filename, Download::NoChecksum() ) ;
CURL *curl = InitCurl( url, 0, hdr ) ;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Download::Callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dl ) ;
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
DoCurl( curl ) ;
}
void GetFile(
const std::string& url,
const std::string& filename,
std::string& md5sum,
const Headers& hdr )
{
}
std::string PostData( const std::string& url, const std::string& data, const Headers& hdr )
{
std::string resp ;
CURL *curl = InitCurl( url, &resp, hdr ) ;
std::string post_data = data ;
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &post_data[0] ) ;
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_data.size() ) ;
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1 ) ;
DoCurl( curl ) ;
return resp;
}
std::string PostDataWithHeader( const std::string& url, const std::string& data, const Headers& hdr )
{
std::string resp ;
CURL *curl = InitCurl( url, &resp, hdr ) ;
std::string post_data = data ;
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &post_data[0] ) ;
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_data.size() ) ;
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L ) ;
curl_easy_setopt(curl, CURLOPT_HEADER, 1L );
try
{
DoCurl( curl ) ;
}
catch ( ... )
{
std::cout << "response = " << resp << std::endl ;
throw ;
}
return resp;
}
std::string PostFile( const std::string& url, const std::string& filename, const Headers& hdr )
{
std::string resp ;
return resp;
}
std::string Put(
const std::string& url,
const std::string& data,
const Headers& hdr )
{
std::string resp ;
CURL *curl = InitCurl( url, &resp, hdr ) ;
std::string put_data = data ;
// set common options
curl_easy_setopt(curl, CURLOPT_HEADER, 1L );
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ;
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadCallback ) ;
curl_easy_setopt(curl, CURLOPT_READDATA , &put_data ) ;
curl_easy_setopt(curl, CURLOPT_INFILESIZE, put_data.size() ) ;
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L ) ;
try
{
DoCurl( curl ) ;
}
catch ( ... )
{
std::cout << "response = " << resp << std::endl ;
throw ;
}
return resp;
}
std::string Escape( const std::string& str )
{
CURL *curl = curl_easy_init();
char *tmp = curl_easy_escape( curl, str.c_str(), str.size() ) ;
std::string result = tmp ;
curl_free( tmp ) ;
curl_easy_cleanup(curl);
return result ;
}
std::string Unescape( const std::string& str )
{
CURL *curl = curl_easy_init();
int r ;
char *tmp = curl_easy_unescape( curl, str.c_str(), str.size(), &r ) ;
std::string result = tmp ;
curl_free( tmp ) ;
curl_easy_cleanup(curl);
return result ;
}
} } // end of namespace
namespace gr {
struct Http::Impl
{
CURL *curl ;
std::string location ;
} ;
Http::Http() :
m_pimpl( new Impl )
{
m_pimpl->curl = ::curl_easy_init();
}
Http::~Http()
{
::curl_easy_cleanup( m_pimpl->curl );
}
std::size_t Http::HeaderCallback( void *ptr, size_t size, size_t nmemb, Http *pthis )
{
std::string line( (char*)ptr, (char*)ptr + size*nmemb ) ;
static const std::string loc = "Location: " ;
std::size_t pos = line.find( loc ) ;
if ( pos != line.npos )
{
std::size_t end_pos = line.find( "\r\n", pos ) ;
pthis->m_pimpl->location = line.substr( loc.size(), end_pos - loc.size() ) ;
}
return size*nmemb ;
}
std::string Http::Put(
const std::string& url,
const std::string& data,
const http::Headers& hdr )
{
CURL *curl = m_pimpl->curl ;
std::string put_data = data ;
std::string resp ;
// set common options
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp ) ;
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ;
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadCallback ) ;
curl_easy_setopt(curl, CURLOPT_READDATA , &put_data ) ;
curl_easy_setopt(curl, CURLOPT_INFILESIZE, put_data.size() ) ;
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &Http::HeaderCallback ) ;
curl_easy_setopt(curl, CURLOPT_WRITEHEADER , this ) ;
// set headers
struct curl_slist *curl_hdr = 0 ;
for ( Headers::const_iterator i = hdr.begin() ; i != hdr.end() ; ++i )
curl_hdr = curl_slist_append( curl_hdr, i->c_str() ) ;
curl_easy_setopt( curl, CURLOPT_HTTPHEADER, curl_hdr ) ;
char error_buf[CURL_ERROR_SIZE] = {} ;
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf ) ;
CURLcode curl_code = curl_easy_perform(curl);
long http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if ( curl_code != CURLE_OK )
{
throw Exception( curl_code, http_code, error_buf ) ;
}
else if (http_code >= 400 )
{
std::cout << "http error " << http_code << " " << resp << std::endl ;
throw Exception( curl_code, http_code, error_buf ) ;
}
return resp ;
}
std::string Http::RedirLocation() const
{
return m_pimpl->location ;
}
} // end of namespace

View File

@ -1,97 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <memory>
#include <string>
#include <stdexcept>
#include <vector>
namespace gr {
namespace http
{
typedef std::vector<std::string> Headers ;
std::string Get( const std::string& url, const Headers& hdr = Headers() ) ;
void GetFile(
const std::string& url,
const std::string& filename,
const Headers& hdr = Headers() ) ;
void GetFile(
const std::string& url,
const std::string& filename,
std::string& md5sum,
const Headers& hdr = Headers() ) ;
std::string PostData(
const std::string& url,
const std::string& data,
const Headers& hdr = Headers() ) ;
std::string PostDataWithHeader(
const std::string& url,
const std::string& data,
const Headers& hdr = Headers() ) ;
std::string PostFile(
const std::string& url,
const std::string& filename,
const Headers& hdr = Headers() ) ;
std::string Put(
const std::string& url,
const std::string& data,
const Headers& hdr = Headers() ) ;
std::string Escape( const std::string& str ) ;
std::string Unescape( const std::string& str ) ;
class Exception : public std::runtime_error
{
public :
Exception( int curl_code, int http_code, const char *err_buf ) ;
private :
static std::string Format( int curl_code, int http_code, const char *err_buf ) ;
} ;
}
class Http
{
public :
Http() ;
~Http() ;
std::string Put(
const std::string& url,
const std::string& data,
const http::Headers& hdr = http::Headers() ) ;
std::string RedirLocation() const ;
private :
static std::size_t HeaderCallback( void *ptr, size_t size, size_t nmemb, Http *pthis ) ;
private :
struct Impl ;
std::auto_ptr<Impl> m_pimpl ;
} ;
} // end of namespace

View File

@ -1,266 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Json.hh"
#include <json/json_tokener.h>
#include <json/linkhash.h>
#include <cassert>
#include <iostream>
#include <sstream>
#include <stdexcept>
namespace gr {
Json::Json( ) :
m_json( ::json_object_new_object() )
{
if ( m_json == 0 )
throw std::runtime_error( "cannot create json object" ) ;
}
template <>
Json::Json( const std::string& str ) :
m_json( ::json_object_new_string( str.c_str() ) )
{
if ( m_json == 0 )
throw std::runtime_error( "cannot create json string \"" + str + "\"" ) ;
// paranoid check
assert( ::json_object_get_string( m_json ) == str ) ;
}
Json Json::Parse( const std::string& str )
{
struct json_object *json = ::json_tokener_parse( str.c_str() ) ;
if ( json == 0 )
throw std::runtime_error( "json parse error" ) ;
return Json( json, NotOwned() ) ;
}
Json::Json( struct json_object *json, NotOwned ) :
m_json( json )
{
assert( m_json != 0 ) ;
}
Json::Json( struct json_object *json ) :
m_json( json )
{
assert( json != 0 ) ;
::json_object_get( m_json ) ;
}
Json::Json( const Json& rhs ) :
m_json( rhs.m_json )
{
assert( m_json != 0 ) ;
::json_object_get( m_json ) ;
}
Json::~Json( )
{
assert( m_json != 0 ) ;
if ( m_json != 0 )
::json_object_put( m_json ) ;
}
Json& Json::operator=( const Json& rhs )
{
Json tmp( rhs ) ;
Swap( tmp ) ;
return *this ;
}
void Json::Swap( Json& other )
{
assert( m_json != 0 ) ;
assert( other.m_json != 0 ) ;
std::swap( m_json, other.m_json ) ;
}
Json Json::operator[]( const std::string& key ) const
{
assert( m_json != 0 ) ;
struct json_object *j = ::json_object_object_get( m_json, key.c_str() ) ;
if ( j == 0 )
throw std::runtime_error( "key: " + key + " is not found in object" ) ;
return Json( j ) ;
}
Json Json::operator[]( const std::size_t& idx ) const
{
assert( m_json != 0 ) ;
struct json_object *j = ::json_object_array_get_idx( m_json, idx ) ;
if ( j == 0 )
{
std::ostringstream ss ;
ss << "index " << idx << " is not found in array" ;
throw std::runtime_error( ss.str() ) ;
}
return Json( j ) ;
}
bool Json::Has( const std::string& key ) const
{
assert( m_json != 0 ) ;
return ::json_object_object_get( m_json, key.c_str() ) != 0 ;
}
void Json::Add( const std::string& key, const Json& json )
{
assert( m_json != 0 ) ;
assert( json.m_json != 0 ) ;
::json_object_get( json.m_json ) ;
::json_object_object_add( m_json, key.c_str(), json.m_json ) ;
}
template <>
bool Json::As<bool>() const
{
assert( m_json != 0 ) ;
return ::json_object_get_boolean( m_json ) ;
}
template <>
bool Json::Is<bool>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_boolean ) ;
}
template <>
std::string Json::As<std::string>() const
{
assert( m_json != 0 ) ;
return ::json_object_get_string( m_json ) ;
}
template <>
bool Json::Is<std::string>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_string ) ;
}
template <>
int Json::As<int>() const
{
assert( m_json != 0 ) ;
return ::json_object_get_int( m_json ) ;
}
template <>
bool Json::Is<int>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_int ) ;
}
std::ostream& operator<<( std::ostream& os, const Json& json )
{
assert( json.m_json != 0 ) ;
return os << ::json_object_to_json_string( json.m_json ) ;
}
Json::Type Json::DataType() const
{
assert( m_json != 0 ) ;
return static_cast<Type>( ::json_object_get_type( m_json ) ) ;
}
template <>
Json::Object Json::As<Json::Object>() const
{
Object result ;
json_object_object_foreach( m_json, key, val )
{
result.insert( Object::value_type( key, Json( val ) ) ) ;
}
return result ;
}
template <>
bool Json::Is<Json::Object>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_object ) ;
}
template <>
Json::Array Json::As<Json::Array>() const
{
std::size_t count = ::json_object_array_length( m_json ) ;
Array result ;
for ( std::size_t i = 0 ; i < count ; ++i )
result.push_back( Json( ::json_object_array_get_idx( m_json, i ) ) ) ;
return result ;
}
template <>
bool Json::Is<Json::Array>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_array ) ;
}
Json Json::FindInArray( const std::string& key, const std::string& value ) const
{
std::size_t count = ::json_object_array_length( m_json ) ;
for ( std::size_t i = 0 ; i < count ; ++i )
{
Json item( ::json_object_array_get_idx( m_json, i ) ) ;
if ( item.Has(key) && item[key].As<std::string>() == value )
return item ;
}
throw std::runtime_error( "cannot find " + key + " = " + value + " in array" ) ;
}
bool Json::FindInArray( const std::string& key, const std::string& value, Json& result ) const
{
try
{
result = FindInArray( key, value ) ;
return true ;
}
catch ( std::runtime_error& )
{
return false ;
}
}
std::string Json::Str() const
{
return As<std::string>() ;
}
}

View File

@ -1,82 +0,0 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <string>
#include <map>
#include <vector>
struct json_object ;
namespace gr {
class Json
{
public :
typedef std::map<std::string, Json> Object ;
typedef std::vector<Json> Array ;
public :
template <typename T>
explicit Json( const T& val ) ;
Json() ;
Json( const Json& rhs ) ;
~Json( ) ;
static Json Parse( const std::string& str ) ;
Json operator[]( const std::string& key ) const ;
Json operator[]( const std::size_t& idx ) const ;
Json& operator=( const Json& rhs ) ;
void Swap( Json& other ) ;
template <typename T>
T As() const ;
// As<std::string>() shortcut
std::string Str() const ;
template <typename T>
bool Is() const ;
bool Has( const std::string& key ) const ;
void Add( const std::string& key, const Json& json ) ;
Json FindInArray( const std::string& key, const std::string& value ) const ;
bool FindInArray( const std::string& key, const std::string& value, Json& result ) const ;
friend std::ostream& operator<<( std::ostream& os, const Json& json ) ;
enum Type { null_type, bool_type, double_type, int_type, object_type, array_type, string_type } ;
Type DataType() const ;
private :
Json( struct json_object *json ) ;
struct NotOwned {} ;
Json( struct json_object *json, NotOwned ) ;
private :
struct json_object *m_json ;
} ;
}

View File

@ -19,8 +19,18 @@
#include "OAuth2.hh"
#include "protocol/HTTP.hh"
#include "protocol/Json.hh"
#include "json/ValResponse.hh"
#include "http/CurlAgent.hh"
#include "http/Header.hh"
#include "util/log/Log.hh"
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
// for debugging
#include <iostream>
@ -30,10 +40,12 @@ namespace gr {
const std::string token_url = "https://accounts.google.com/o/oauth2/token" ;
OAuth2::OAuth2(
http::Agent* agent,
const std::string& refresh_code,
const std::string& client_id,
const std::string& client_secret ) :
m_refresh( refresh_code ),
m_agent( agent ),
m_client_id( client_id ),
m_client_secret( client_secret )
{
@ -41,44 +53,160 @@ OAuth2::OAuth2(
}
OAuth2::OAuth2(
http::Agent* agent,
const std::string& client_id,
const std::string& client_secret ) :
m_agent( agent ),
m_port( 0 ),
m_socket( -1 ),
m_client_id( client_id ),
m_client_secret( client_secret )
{
}
void OAuth2::Auth( const std::string& auth_code )
OAuth2::~OAuth2()
{
if ( m_socket >= 0 )
{
close( m_socket );
m_socket = -1;
}
}
bool OAuth2::Auth( const std::string& auth_code )
{
std::string post =
"code=" + auth_code +
"&client_id=" + m_client_id +
"&client_secret=" + m_client_secret +
"&redirect_uri=" + "urn:ietf:wg:oauth:2.0:oob" +
"&redirect_uri=http%3A%2F%2Flocalhost:" + std::to_string( m_port ) + "%2Fauth" +
"&grant_type=authorization_code" ;
Json resp = Json::Parse( http::PostData( token_url, post ) ) ;
m_access = resp["access_token"].As<std::string>() ;
m_refresh = resp["refresh_token"].As<std::string>() ;
http::ValResponse resp ;
long code = m_agent->Post( token_url, post, &resp, http::Header() ) ;
if ( code >= 200 && code < 300 )
{
Val jresp = resp.Response() ;
m_access = jresp["access_token"].Str() ;
m_refresh = jresp["refresh_token"].Str() ;
}
else
{
Log( "Failed to obtain auth token: HTTP %1%, body: %2%",
code, m_agent->LastError(), log::error ) ;
return false;
}
return true;
}
std::string OAuth2::MakeAuthURL(
const std::string& client_id,
const std::string& state )
std::string OAuth2::MakeAuthURL()
{
using gr::http::Escape ;
if ( !m_port )
{
sockaddr_storage addr = { 0 };
addr.ss_family = AF_INET;
m_socket = socket( AF_INET, SOCK_STREAM, 0 );
if ( m_socket < 0 )
throw std::runtime_error( std::string("socket: ") + strerror(errno) );
if ( bind( m_socket, (sockaddr*)&addr, sizeof( addr ) ) < 0 )
{
close( m_socket );
m_socket = -1;
throw std::runtime_error( std::string("bind: ") + strerror(errno) );
}
socklen_t len = sizeof( addr );
if ( getsockname( m_socket, (sockaddr *)&addr, &len ) == -1 )
{
close( m_socket );
m_socket = -1;
throw std::runtime_error( std::string("getsockname: ") + strerror(errno) );
}
m_port = ntohs(((sockaddr_in*)&addr)->sin_port);
if ( listen( m_socket, 128 ) < 0 )
{
close( m_socket );
m_socket = -1;
m_port = 0;
throw std::runtime_error( std::string("listen: ") + strerror(errno) );
}
}
return "https://accounts.google.com/o/oauth2/auth"
"?scope=" +
Escape( "https://www.googleapis.com/auth/userinfo.email" ) + "+" +
Escape( "https://www.googleapis.com/auth/userinfo.profile" ) + "+" +
Escape( "https://docs.google.com/feeds/" ) + "+" +
Escape( "https://docs.googleusercontent.com/" ) + "+" +
Escape( "https://spreadsheets.google.com/feeds/" ) + /*"+" +
Escape( "https://www.googleapis.com/auth/drive.file/" ) +*/
"&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
"?scope=" + m_agent->Escape( "https://www.googleapis.com/auth/drive" ) +
"&redirect_uri=http%3A%2F%2Flocalhost:" + std::to_string( m_port ) + "%2Fauth" +
"&response_type=code"
"&client_id=" + client_id ;
"&client_id=" + m_client_id ;
}
bool OAuth2::GetCode( )
{
sockaddr_storage addr = { 0 };
int peer_fd = -1;
while ( peer_fd < 0 )
{
socklen_t peer_addr_size = sizeof( addr );
peer_fd = accept( m_socket, (sockaddr*)&addr, &peer_addr_size );
if ( peer_fd == -1 && errno != EAGAIN && errno != EINTR )
throw std::runtime_error( std::string("accept: ") + strerror(errno) );
}
fcntl( peer_fd, F_SETFL, fcntl( peer_fd, F_GETFL, 0 ) | O_NONBLOCK );
struct pollfd pfd = (struct pollfd){
.fd = peer_fd,
.events = POLLIN|POLLRDHUP,
};
char buf[4096];
std::string request;
while ( true )
{
pfd.revents = 0;
poll( &pfd, 1, -1 );
if ( pfd.revents & POLLRDHUP )
break;
int r = 1;
while ( r > 0 )
{
r = read( peer_fd, buf, sizeof( buf ) );
if ( r > 0 )
request += std::string( buf, r );
else if ( r == 0 )
break;
else if ( errno != EAGAIN && errno != EINTR )
throw std::runtime_error( std::string("read: ") + strerror(errno) );
}
if ( r == 0 || ( r < 0 && request.find( "\n" ) > 0 ) ) // GET ... HTTP/1.1\r\n
break;
}
bool ok = false;
if ( request.substr( 0, 10 ) == "GET /auth?" )
{
std::string line = request;
int p = line.find( "\n" );
if ( p > 0 )
line = line.substr( 0, p );
p = line.rfind( " " );
if ( p > 0 )
line = line.substr( 0, p );
p = line.find( "code=" );
if ( p > 0 )
line = line.substr( p+5 );
p = line.find( "&" );
if ( p > 0 )
line = line.substr( 0, p );
ok = Auth( line );
}
std::string response = ( ok
? "Authenticated successfully. Please close the page"
: "Authentication error. Please try again" );
response = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Connection: close\r\n"
"\r\n"+
response+
"\r\n";
write( peer_fd, response.c_str(), response.size() );
close( peer_fd );
return ok;
}
void OAuth2::Refresh( )
@ -89,8 +217,18 @@ void OAuth2::Refresh( )
"&client_secret=" + m_client_secret +
"&grant_type=refresh_token" ;
Json resp = Json::Parse( http::PostData( token_url, post ) ) ;
m_access = resp["access_token"].As<std::string>() ;
http::ValResponse resp ;
long code = m_agent->Post( token_url, post, &resp, http::Header() ) ;
if ( code >= 200 && code < 300 )
m_access = resp.Response()["access_token"].Str() ;
else
{
Log( "Failed to refresh auth token: HTTP %1%, body: %2%",
code, m_agent->LastError(), log::error ) ;
BOOST_THROW_EXCEPTION( AuthFailed() );
}
}
std::string OAuth2::RefreshToken( ) const

View File

@ -19,42 +19,53 @@
#pragma once
#include "http/Agent.hh"
#include "util/Exception.hh"
#include <string>
#include <memory>
namespace gr {
class OAuth2
{
public :
struct AuthFailed : virtual Exception {} ;
public :
OAuth2(
http::Agent* agent,
const std::string& client_id,
const std::string& client_secret ) ;
OAuth2(
http::Agent* agent,
const std::string& refresh_code,
const std::string& client_id,
const std::string& client_secret ) ;
~OAuth2( ) ;
std::string Str() const ;
static std::string MakeAuthURL(
const std::string& client_id,
const std::string& state = std::string() ) ;
void Auth( const std::string& auth_code ) ;
std::string MakeAuthURL() ;
bool Auth( const std::string& auth_code ) ;
void Refresh( ) ;
bool GetCode( ) ;
std::string RefreshToken( ) const ;
std::string AccessToken( ) const ;
// adding HTTP auth header
std::string HttpHeader( ) const ;
private :
std::string m_access ;
std::string m_refresh ;
http::Agent* m_agent ;
int m_port ;
int m_socket ;
const std::string m_client_id ;
const std::string m_client_secret ;
} ;
} // end of namespace

View File

@ -0,0 +1,78 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <cstddef>
namespace gr {
/*! \brief get the begin iterator from an array
\internal
This function returns the begin "iterator" of an array. It is useful to
treat an array like an STL container.
For example:
\code
int array[10] = { 1, 2, 3, 4, 5 } ;
std::vector<int> v ;
std::copy( Begin(array), End(array), std::back_inserter( v ) ;
\endcode
\param array reference to the array
\return the begin iterator of the array. i.e. \a array itself
\sa End(), Count()
*/
template <typename T, std::size_t n>
T* Begin( T (&array)[n] )
{
return array ;
}
/*! \brief get the end iterator from an array
\internal
This function returns the end "iterator" of an array. It is useful to
treat an array like an STL container.
\param array reference to the array
\return the end iterator of the array. i.e. \a array+n
\sa Begin(), Count()
*/
template <typename T, std::size_t n>
T* End( T (&array)[n] )
{
return array + n ;
}
/*! \brief get the number of elements in the array
\internal
This function will return the number of elements in the array.
\return the number of elements in the array
\sa Begin(), End()
*/
template <typename T, std::size_t n>
std::size_t Count( T (&array)[n] )
{
return n ;
}
} // end of namespace

View File

@ -0,0 +1,105 @@
/*
A stream representing a concatenation of several underlying streams
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include "ConcatStream.hh"
namespace gr {
ConcatStream::ConcatStream() :
m_size( 0 ), m_pos( 0 ), m_cur( 0 )
{
}
std::size_t ConcatStream::Read( char *data, std::size_t size )
{
std::size_t done = 0, l;
while ( done < size && m_cur < m_streams.size() )
{
l = m_streams[m_cur]->Read( data+done, ( size-done < m_sizes[m_cur]-m_pos ? size-done : m_sizes[m_cur]-m_pos ) );
done += l;
m_pos += l;
if ( !l )
{
// current stream was truncated in the meantime, pad output with zeros
memset( data+done, 0, m_sizes[m_cur]-m_pos );
done += m_sizes[m_cur]-m_pos;
m_pos = m_sizes[m_cur];
}
if ( m_pos >= m_sizes[m_cur] )
{
m_cur++;
if ( m_cur < m_streams.size() )
m_streams[m_cur]->Seek( 0, 0 );
}
}
return done ;
}
std::size_t ConcatStream::Write( const char *data, std::size_t size )
{
return 0 ;
}
off_t ConcatStream::Seek( off_t offset, int whence )
{
if ( whence == 1 )
offset += m_pos;
else if ( whence == 2 )
offset += Size();
if ( (u64_t)offset > Size() )
offset = Size();
m_cur = 0;
m_pos = offset;
if ( m_streams.size() )
{
while ( (u64_t)offset > m_sizes[m_cur] )
m_cur++;
m_streams[m_cur]->Seek( offset - ( m_cur > 0 ? m_sizes[m_cur-1] : 0 ), 0 );
}
return m_pos ;
}
off_t ConcatStream::Tell() const
{
return m_pos ;
}
u64_t ConcatStream::Size() const
{
return m_size ;
}
void ConcatStream::Append( SeekStream *stream )
{
if ( stream )
{
u64_t size = stream->Size();
if ( size > 0 )
{
// "fix" stream size at the moment of adding so further changes of underlying files
// don't affect the total size of resulting stream...
m_size += size;
m_streams.push_back( stream );
m_sizes.push_back( m_size );
}
}
}
} // end of namespace

View File

@ -0,0 +1,49 @@
/*
A stream representing a concatenation of several underlying streams
Copyright (C) 2015 Vitaliy Filippov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "DataStream.hh"
#include <vector>
namespace gr {
class ConcatStream : public SeekStream
{
public :
ConcatStream() ;
std::size_t Read( char *data, std::size_t size ) ;
std::size_t Write( const char *data, std::size_t size ) ;
off_t Seek( off_t offset, int whence ) ;
off_t Tell() const ;
u64_t Size() const ;
void Append( SeekStream *stream ) ;
private :
std::vector<SeekStream*> m_streams ;
std::vector<u64_t> m_sizes ;
u64_t m_size, m_pos ;
size_t m_cur ;
} ;
} // end of namespace

119
libgrive/src/util/Config.cc Normal file
View File

@ -0,0 +1,119 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Config.hh"
#include "util/File.hh"
#include "json/JsonWriter.hh"
#include "json/JsonParser.hh"
#include <boost/program_options.hpp>
#include <iostream>
#include <iterator>
namespace po = boost::program_options;
namespace gr {
const std::string default_filename = ".grive";
const char *env_name = "GR_CONFIG";
const std::string default_root_folder = ".";
Config::Config( const po::variables_map& vm )
{
if ( vm.count( "id" ) > 0 )
m_cmd.Add( "id", Val( vm["id"].as<std::string>() ) ) ;
if ( vm.count( "secret" ) > 0 )
m_cmd.Add( "secret", Val( vm["secret"].as<std::string>() ) ) ;
m_cmd.Add( "new-rev", Val(vm.count("new-rev") > 0) ) ;
m_cmd.Add( "force", Val(vm.count("force") > 0 ) ) ;
m_cmd.Add( "path", Val(vm.count("path") > 0
? vm["path"].as<std::string>()
: default_root_folder ) ) ;
m_cmd.Add( "dir", Val(vm.count("dir") > 0
? vm["dir"].as<std::string>()
: "" ) ) ;
if ( vm.count( "ignore" ) > 0 )
m_cmd.Add( "ignore", Val( vm["ignore"].as<std::string>() ) );
m_cmd.Add( "no-remote-new", Val( vm.count( "no-remote-new" ) > 0 || vm.count( "upload-only" ) > 0 ) );
m_cmd.Add( "upload-only", Val( vm.count( "upload-only" ) > 0 ) );
m_cmd.Add( "no-delete-remote", Val( vm.count( "no-delete-remote" ) > 0 ) );
m_path = GetPath( fs::path(m_cmd["path"].Str()) ) ;
m_file = Read( ) ;
}
fs::path Config::GetPath( const fs::path& root_path )
{
// config file will be (in order of preference)
// value specified in environment string
// value specified in defaultConfigFileName in path from commandline --path
// value specified in defaultConfigFileName in current directory
const char *env = ::getenv( env_name ) ;
return root_path / (env ? env : default_filename) ;
}
const fs::path Config::Filename() const
{
return m_path ;
}
void Config::Save( )
{
gr::File file( m_path.string(), 0600 ) ;
JsonWriter wr( &file ) ;
m_file.Visit( &wr ) ;
}
void Config::Set( const std::string& key, const Val& value )
{
m_file.Set( key, value ) ;
}
Val Config::Get( const std::string& key ) const
{
return m_cmd.Has(key) ? m_cmd[key] : m_file[key] ;
}
Val Config::GetAll() const
{
Val::Object obj = m_file.AsObject() ;
Val::Object cmd_obj = m_cmd.AsObject() ;
for ( Val::Object::iterator i = cmd_obj.begin() ; i != cmd_obj.end() ; ++i )
obj[i->first] = i->second ;
return Val( obj ) ;
}
Val Config::Read()
{
try
{
gr::File file(m_path) ;
return ParseJson( file ) ;
}
catch ( Exception& e )
{
return Val() ;
}
}
} // end of namespace

View File

@ -0,0 +1,67 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include "Exception.hh"
#include "FileSystem.hh"
#include "json/Val.hh"
namespace boost
{
namespace program_options
{
class variables_map ;
}
}
namespace gr {
class Config
{
public :
struct Error : virtual Exception {} ;
typedef boost::error_info<struct FileTag, std::string> File ;
Config( const boost::program_options::variables_map& vm ) ;
const fs::path Filename() const ;
void Set( const std::string& key, const Val& value ) ;
Val Get( const std::string& key ) const ;
Val GetAll() const ;
void Save() ;
private :
Val Read( ) ;
static fs::path GetPath( const fs::path& root_path ) ;
private :
//! config file path
fs::path m_path;
//! config values loaded from config file
Val m_file ;
//! config values from command line
Val m_cmd ;
} ;
} // end of namespace

View File

@ -19,37 +19,86 @@
#include "Crypt.hh"
#include "File.hh"
#include "Exception.hh"
#include "MemMap.hh"
#include <iomanip>
#include <sstream>
// dependent libraries
#include <openssl/evp.h>
#include <gcrypt.h>
#include <boost/throw_exception.hpp>
namespace gr { namespace crypt {
std::string MD5( std::streambuf *file )
// map 4MB of data at a time
const u64_t read_size = 1024 * 4096 ;
struct MD5::Impl
{
char buf[64 * 1024] ;
EVP_MD_CTX md ;
EVP_MD_CTX_init( &md );
EVP_DigestInit_ex( &md, EVP_md5(), 0 ) ;
std::size_t count = 0 ;
while ( (count = file->sgetn( buf, sizeof(buf) )) > 0 )
gcry_md_hd_t hd ;
} ;
MD5::MD5() : m_impl( new Impl )
{
::gcry_error_t err = ::gcry_md_open( &m_impl->hd, GCRY_MD_MD5, 0 ) ;
if ( err != GPG_ERR_NO_ERROR )
{
EVP_DigestUpdate( &md, buf, count ) ;
BOOST_THROW_EXCEPTION( Exception()
<< GCryptErr_( ::gcry_strerror(err) )
<< GCryptApi_( "gcry_md_open" )
) ;
}
unsigned int md5_size = EVP_MAX_MD_SIZE ;
unsigned char md5[EVP_MAX_MD_SIZE] ;
EVP_DigestFinal_ex( &md, md5, &md5_size ) ;
}
MD5::~MD5()
{
::gcry_md_close( m_impl->hd ) ;
}
void MD5::Write( const void *data, std::size_t size )
{
::gcry_md_write( m_impl->hd, data, size ) ;
}
std::string MD5::Get() const
{
unsigned char *md5 = ::gcry_md_read( m_impl->hd, GCRY_MD_MD5 ) ;
unsigned int len = ::gcry_md_get_algo_dlen(GCRY_MD_MD5) ;
// format the MD5 string
std::ostringstream ss ;
for ( unsigned int i = 0 ; i < md5_size ; i++ )
for ( unsigned int i = 0 ; i < len ; i++ )
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(md5[i]) ;
return ss.str() ;
}
std::string MD5::Get( const fs::path& file )
{
try
{
File sfile( file ) ;
return Get( sfile ) ;
}
catch ( File::Error& )
{
return "" ;
}
}
std::string MD5::Get( File& file )
{
MD5 crypt ;
u64_t size = file.Size() ;
for ( u64_t i = 0 ; i < size ; i += read_size )
{
MemMap map( file, i, static_cast<std::size_t>(std::min(read_size, size-i)) ) ;
crypt.Write( map.Addr(), map.Length() ) ;
}
return crypt.Get() ;
}
} } // end of namespaces

View File

@ -19,14 +19,38 @@
#pragma once
#include "util/Exception.hh"
#include <string>
#include <iosfwd>
#include <memory>
#include <boost/filesystem.hpp>
namespace gr {
namespace crypt
{
std::string MD5( std::streambuf *file ) ;
}
class File ;
} // end of namespace gr
namespace crypt {
class MD5
{
public :
typedef boost::error_info<struct GCryptErr, std::string> GCryptErr_ ;
typedef boost::error_info<struct GCryptApi, std::string> GCryptApi_ ;
public :
MD5() ;
~MD5() ;
static std::string Get( File& file ) ;
static std::string Get( const boost::filesystem::path& file ) ;
void Write( const void *data, std::size_t size ) ;
std::string Get() const ;
private :
struct Impl ;
std::unique_ptr<Impl> m_impl ;
} ;
} } // end of namespace gr

View File

@ -0,0 +1,60 @@
/*
webwrite: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
#include <cstddef>
#include "util/Types.hh"
namespace gr {
/** \brief Encapsulation of data streams. Useful for unit tests.
This class provides two functions: Read() and Write().
*/
class DataStream
{
protected :
virtual ~DataStream() {}
public :
/** Reading from the stream. The caller indicates that it wants
to read `size` bytes and must provide enough space pointed
by `data`.
\param data Buffer to hold the data read from the stream
Must have at least `size` bytes.
\param size Number of bytes the caller wants to read.
\throw wb::Exception In case of any error.
\return The number of byte actually read from the stream.
0 indicates the end of stream, i.e. you will
still get 0 if you call again.
*/
virtual std::size_t Read( char *data, std::size_t size ) = 0 ;
virtual std::size_t Write( const char *data, std::size_t size ) = 0 ;
} ;
class SeekStream: public DataStream
{
public :
virtual off_t Seek( off_t offset, int whence ) = 0 ;
virtual off_t Tell() const = 0 ;
virtual u64_t Size() const = 0 ;
} ;
} // end of namespace

View File

@ -19,10 +19,20 @@
#include "DateTime.hh"
#include "Exception.hh"
// boost headers
#include <boost/throw_exception.hpp>
#include <boost/exception/errinfo_api_function.hpp>
#include <boost/exception/errinfo_at_line.hpp>
#include <boost/exception/errinfo_errno.hpp>
#include <boost/exception/info.hpp>
#include <sstream>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <iomanip>
#include <time.h>
@ -53,10 +63,39 @@ DateTime::DateTime( const std::string& iso ) :
}
}
DateTime::DateTime( std::time_t sec, unsigned long nsec ) :
m_sec ( sec + nsec / 1000000000 ),
m_nsec ( nsec % 1000000000 )
DateTime::DateTime( std::time_t sec, unsigned long nsec )
{
Assign( sec, nsec ) ;
}
void DateTime::Assign( std::time_t sec, unsigned long nsec )
{
m_sec = sec + nsec / 1000000000 ;
m_nsec = nsec % 1000000000 ;
}
DateTime DateTime::Now()
{
struct timeval tv = {} ;
if ( ::gettimeofday( &tv, 0 ) != 0 )
{
BOOST_THROW_EXCEPTION(
Exception()
<< boost::errinfo_api_function("gettimeofday")
<< boost::errinfo_errno(errno)
) ;
}
return DateTime( tv.tv_sec, tv.tv_usec * 1000 ) ;
}
std::string DateTime::Format( const std::string& format ) const
{
struct tm tp = Tm() ;
char tmp[1024] ;
std::size_t count = ::strftime( tmp, sizeof(tmp), format.c_str(), &tp ) ;
return count > 0 ? std::string( tmp, count ) : "" ;
}
struct tm DateTime::Tm() const
@ -129,4 +168,17 @@ bool DateTime::operator<=( const DateTime& dt ) const
return !( *this > dt ) ;
}
void DateTime::Swap( DateTime& dt )
{
std::swap( m_sec, dt.m_sec ) ;
std::swap( m_nsec, dt.m_nsec ) ;
}
std::string DateTime::ToString() const
{
std::ostringstream ss ;
ss << *this ;
return ss.str() ;
}
} // end of namespace

View File

@ -34,9 +34,14 @@ public :
explicit DateTime( const std::string& iso ) ;
explicit DateTime( std::time_t sec, unsigned long nsec = 0 ) ;
void Assign( std::time_t sec, unsigned long nsec = 0 ) ;
static DateTime Now() ;
std::time_t Sec( ) const ;
unsigned long NanoSec( ) const ;
std::string Format( const std::string& format ) const ;
tm Tm() const ;
timeval Tv() const ;
@ -47,6 +52,9 @@ public :
bool operator<( const DateTime& dt ) const ;
bool operator<=( const DateTime& dt ) const ;
void Swap( DateTime& dt ) ;
std::string ToString() const ;
private :
std::time_t m_sec ;
unsigned long m_nsec ;

View File

@ -0,0 +1,33 @@
/*
grive: an GPL program to sync a local directory with Google Drive
Copyright (C) 2012 Wan Wai Ho
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
namespace gr {
struct Destroy
{
template <typename T>
void operator()( T *t ) const
{
delete t ;
}
} ;
} // end of namespace

Some files were not shown because too many files have changed in this diff Show More