diff --git a/Makefile b/Makefile index 9a040386..ceaacaaa 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,14 @@ DC = dmd -DFLAGS = -unittest -debug -g -od./bin -of./bin/$@ -L-lcurl -L-lsqlite3 +DFLAGS = -debug -g -gs -od./bin -of./bin/$@ -L-lcurl -L-lsqlite3 -L-ldl SOURCES = \ + /usr/include/dlang/dmd/core/sys/posix/poll.d \ + /usr/include/dlang/dmd/etc/c/curl.d \ + /usr/include/dlang/dmd/std/net/curl.d \ src/cache.d \ src/config.d \ src/main.d \ + src/monitor.d \ src/onedrive.d \ src/sqlite.d \ src/sync.d \ diff --git a/src/main.d b/src/main.d index b8eaa703..1b61079c 100644 --- a/src/main.d +++ b/src/main.d @@ -1,5 +1,5 @@ import std.file; -import config, onedrive, sync; +import config, monitor, onedrive, sync; private string configFile = "./onedrive.conf"; private string refreshTokenFile = "refresh_token"; @@ -19,12 +19,28 @@ void main() auto sync = new SyncEngine(cfg, onedrive); sync.applyDifferences(); + sync.uploadDifferences(); - /*import std.stdio; - import std.net.curl; - try { - onedrive.simpleUpload("a.txt", "a.txt", "error").toPrettyString.writeln; - } catch (CurlException e) { - writeln("exc ", e.msg); - }*/ + Monitor m; + import std.stdio; + m.onDirCreated = delegate(string path) { + writeln("Directory created: ", path); + sync.createFolderItem(path); + sync.uploadDifferences(path); + }; + m.onFileChanged = delegate(string path) { + writeln("File changed: ", path); + sync.uploadDifference2(path); + }; + m.onDelete = delegate(string path) { + sync.deleteByPath(path); + }; + m.onMove = delegate(string from, string to) { + sync.moveItem(from, to); + }; + m.init(); + string syncDir = cfg.get("sync_dir"); + chdir(syncDir); + m.addRecursive("test"); + while (true) m.update(); } diff --git a/src/monitor.d b/src/monitor.d index 3482747c..1224055a 100644 --- a/src/monitor.d +++ b/src/monitor.d @@ -38,10 +38,10 @@ struct Monitor // buffer to receive the inotify events private void[] buffer; - void function(string path) onDirCreated; - void function(string path) onFileChanged; - void function(string path) onDelete; - void function(string from, string to) onMove; + void delegate(string path) onDirCreated; + void delegate(string path) onFileChanged; + void delegate(string path) onDelete; + void delegate(string from, string to) onMove; @disable this(this); diff --git a/src/sync.d b/src/sync.d index 4247113c..b8250ed4 100644 --- a/src/sync.d +++ b/src/sync.d @@ -1,5 +1,5 @@ import core.exception: RangeError; -import std.stdio, std.file, std.json; +import std.file, std.json, std.path, std.stdio; import cache, config, onedrive, util; private string statusTokenFile = "status_token"; @@ -162,6 +162,38 @@ final class SyncEngine } } + private void cacheItem(JSONValue item) + { + string id = item["id"].str; + ItemType type; + if (isItemDeleted(item)) { + itemCache.deleteById(id); + } else if (isItemFile(item)) { + type = ItemType.file; + } else if (isItemFolder(item)) { + type = ItemType.dir; + } else { + writeln("The item is neither a file nor a directory, skipping"); + return; + } + string name = item["name"].str; + string eTag = item["eTag"].str; + string cTag = item["cTag"].str; + string mtime = item["fileSystemInfo"].object["lastModifiedDateTime"].str; + string parentId = item["parentReference"].object["id"].str; + string crc32; + if (type == ItemType.file) { + try { + crc32 = item["file"].object["hashes"].object["crc32Hash"].str; + } catch (JSONException e) { + writeln("The hash is not available"); + } catch (RangeError e) { + writeln("The crc32 hash is not available"); + } + } + itemCache.insert(id, name, type, eTag, cTag, mtime, parentId, crc32); + } + private void applyDelete(Item item) { if (exists(item.path)) { @@ -314,6 +346,19 @@ final class SyncEngine chdir(currDir); } + public void uploadDifferences(string path) + { + assert(isDir(path)); + Item item; + foreach (DirEntry entry; dirEntries(path, SpanMode.breadth, false)) { + if (itemCache.selectByPath(entry.name, item)) { + uploadDifference(item); + } else { + uploadNewItem(entry.name); + } + } + } + private void uploadDifference(Item item) { writeln(item.path); @@ -333,16 +378,8 @@ final class SyncEngine } else { deleteItem(item); writeln("Uploading ..."); - JSONValue returnedItem = onedrive.simpleUpload(item.path, item.path); - string id = returnedItem["id"].str; - string eTag = returnedItem["eTag"].str; - writeln("Updating last modified time ..."); - JSONValue mtime = [ - "fileSystemInfo": JSONValue([ - "lastModifiedDateTime": timeLastModified(item.path).toUTC().toISOExtString() - ]) - ]; - onedrive.updateById(id, mtime, eTag); + auto res = onedrive.simpleUpload(item.path, item.path); + cacheItem(res); } break; } @@ -360,10 +397,23 @@ final class SyncEngine } } + // HACK + void uploadDifference2(const(char)[] path) + { + assert(isFile(path)); + Item item; + if (itemCache.selectByPath(path, item)) { + uploadDifference(item); + } else { + uploadNewItem(path); + } + } + private void deleteItem(Item item) { writeln("Deleting ..."); onedrive.deleteById(item.id, item.eTag); + itemCache.deleteById(item.id); } private void updateItem(Item item) @@ -377,9 +427,10 @@ final class SyncEngine if (item.type == ItemType.file && !testCrc32(item.path, item.crc32)) { assert(isFile(item.path)); writeln("Uploading ..."); - JSONValue returnedItem = onedrive.simpleUpload(item.path, item.path, item.eTag); - id = returnedItem["id"].str; - eTag = returnedItem["eTag"].str; + JSONValue res = onedrive.simpleUpload(item.path, item.path, item.eTag); + cacheItem(res); + id = res["id"].str; + eTag = res["eTag"].str; } updateItemLastModifiedTime(id, eTag, localModifiedTime.toUTC()); } else { @@ -387,13 +438,13 @@ final class SyncEngine } } - private void createFolderItem(const(char)[] path) + void createFolderItem(const(char)[] path) { - import std.path; writeln("Creating folder ..."); folderItem["name"] = baseName(path).idup; folderItem["fileSystemInfo"].object["lastModifiedDateTime"] = timeLastModified(path).toUTC().toISOExtString(); - onedrive.createByPath(dirName(path), folderItem); + auto res = onedrive.createByPath(dirName(path), folderItem); + cacheItem(res); } private void updateItemLastModifiedTime(const(char)[] id, const(char)[] eTag, SysTime mtime) @@ -404,7 +455,8 @@ final class SyncEngine "lastModifiedDateTime": mtime.toISOExtString() ]) ]; - onedrive.updateById(id, mtimeJson, eTag); + auto res = onedrive.updateById(id, mtimeJson, eTag); + cacheItem(res); } private void uploadNewItem(const(char)[] path) @@ -412,12 +464,39 @@ final class SyncEngine assert(exists(path)); if (isFile(path)) { writeln("Uploading file ..."); - JSONValue returnedItem = onedrive.simpleUpload(path.dup, path); - string id = returnedItem["id"].str; - string eTag = returnedItem["eTag"].str; + JSONValue res = onedrive.simpleUpload(path.dup, path); + cacheItem(res); + string id = res["id"].str; + string eTag = res["eTag"].str; updateItemLastModifiedTime(id, eTag, timeLastModified(path).toUTC()); } else { createFolderItem(path); } } + + void moveItem(const(char)[] from, string to) + { + writeln("Moving ", from, " to ", to, " ..."); + Item item; + if (!itemCache.selectByPath(from, item)) { + throw new SyncException("Can't move a non synced item"); + } + JSONValue diff = ["name": baseName(to)]; + diff["parentReference"] = JSONValue([ + "path": "/drive/root:/" ~ dirName(to) + ]); + writeln(diff.toPrettyString()); + auto res = onedrive.updateById(item.id, diff, item.eTag); + cacheItem(res); + } + + void deleteByPath(const(char)[] path) + { + writeln("Deleting: ", path); + Item item; + if (!itemCache.selectByPath(path, item)) { + throw new SyncException("Can't delete a non synced item"); + } + deleteItem(item); + } }