command line options

This commit is contained in:
skilion 2015-09-14 19:21:06 +02:00
parent 424e77e4b5
commit ff07f13cd6
4 changed files with 96 additions and 53 deletions

View file

@ -1,5 +1,5 @@
DC = dmd DC = dmd
DFLAGS = -debug -g -gs -od./bin -of./bin/$@ -L-lcurl -L-lsqlite3 -L-ldl DFLAGS = -unittest -debug -g -gs -od./bin -of./bin/$@ -L-lcurl -L-lsqlite3 -L-ldl
SOURCES = \ SOURCES = \
/usr/include/dlang/dmd/core/sys/posix/poll.d \ /usr/include/dlang/dmd/core/sys/posix/poll.d \

View file

@ -1,6 +1,6 @@
import std.regex, std.stdio, std.file; import std.file, std.regex, std.stdio;
final class Config struct Config
{ {
private string filename; private string filename;
private string[string] values; private string[string] values;
@ -8,12 +8,16 @@ final class Config
this(string filename) this(string filename)
{ {
this.filename = filename; this.filename = filename;
load();
} }
string get(string key) string get(string key)
{ {
return values[key]; import core.exception;
try {
return values[key];
} catch (RangeError e) {
throw new Exception("Missing config value: " ~ key);
}
} }
void set(string key, string value) void set(string key, string value)
@ -24,16 +28,17 @@ final class Config
void load() void load()
{ {
values = null; values = null;
scope (failure) return;
auto file = File(filename, "r"); auto file = File(filename, "r");
auto r = regex("(?:^\\s*)(\\w+)(?:\\s*=\\s*\")(.*)(?:\"\\s*$)"); auto r = regex("(?:^\\s*)(\\w+)(?:\\s*=\\s*\")(.*)(?:\"\\s*$)");
foreach (line; file.byLine()) { foreach (line; file.byLine()) {
auto c = matchFirst(line, r); auto c = matchFirst(line, r);
if (!c.empty) { if (!c.empty) {
c.popFront(); // skip whole match c.popFront(); // skip the whole match
string key = c.front.dup; string key = c.front.dup;
c.popFront(); c.popFront();
values[key] = c.front.dup; values[key] = c.front.dup;
} else {
writeln("Malformed config line: ", line);
} }
} }
} }
@ -53,7 +58,7 @@ final class Config
unittest unittest
{ {
auto cfg = new Config("/tmp/test.conf"); auto cfg = Config(tempDir() ~ "/test.conf");
cfg.set("test1", "1"); cfg.set("test1", "1");
cfg.set("test2", "2"); cfg.set("test2", "2");
cfg.set("test1", "3"); cfg.set("test1", "3");

View file

@ -1,46 +1,89 @@
import std.file; import std.getopt, std.file, std.process, std.stdio;
import config, monitor, onedrive, sync; import config, monitor, onedrive, sync;
private string configFile = "./onedrive.conf"; string ver = "1.0";
private string refreshTokenFile = "refresh_token";
void main() void main(string[] args)
{ {
auto cfg = new Config(configFile); bool monitor, resync, resetLocal, resetRemote, verbose;
auto onedrive = new OneDriveApi(cfg.get("client_id"), cfg.get("client_secret"));
onedrive.onRefreshToken = (string refreshToken) { std.file.write(refreshTokenFile, refreshToken); };
try { try {
string refreshToken = readText(refreshTokenFile); writeln("OneDrive Client for Linux v", ver);
auto opt = getopt(
args,
"monitor|m", "Keep monitoring for local and remote changes.", &monitor,
"resync", "Perform a full synchronization.", &resync,
"verbose|v", "Print more details, useful for debugging.", &verbose
);
if (opt.helpWanted) {
defaultGetoptPrinter("Available options:", opt.options);
return;
}
} catch (GetOptException e) {
writeln(e.msg);
writeln("Try 'onedrive -h' for more information.");
return;
}
string homeDirName = environment["HOME"];
string configDirName = environment.get("XDG_CONFIG_HOME", homeDirName ~ "/.config") ~ "/onedrive";
string configFilePath = configDirName ~ "/config";
string refreshTokenFilePath = configDirName ~ "/refresh_token";
string statusTokenFilePath = configDirName ~ "/status_token";
string databaseFilePath = configDirName ~ "/database";
if (resync || resetLocal || resetRemote) {
if (verbose) writeln("Deleting the current status ...");
if (exists(databaseFilePath)) remove(databaseFilePath);
if (exists(statusTokenFilePath)) remove(statusTokenFilePath);
}
if (verbose) writeln("Loading config ...");
auto cfg = config.Config(configFilePath);
cfg.load();
if (verbose) writeln("Initializing the OneDrive API ...");
auto onedrive = new OneDriveApi(cfg, verbose);
onedrive.onRefreshToken = (string refreshToken) {
std.file.write(refreshTokenFilePath, refreshToken);
};
try {
string refreshToken = readText(refreshTokenFilePath);
onedrive.setRefreshToken(refreshToken); onedrive.setRefreshToken(refreshToken);
} catch (FileException e) { } catch (FileException e) {
onedrive.authorize(); onedrive.authorize();
} }
// TODO check if the token is valid
if (verbose) writeln("Initializing the Synchronization Engine ...");
auto sync = new SyncEngine(cfg, onedrive); auto sync = new SyncEngine(cfg, onedrive);
sync.applyDifferences(); sync.applyDifferences();
sync.uploadDifferences(); sync.uploadDifferences();
Monitor m; if (monitor) {
import std.stdio; if (verbose) writeln("Monitoring for changes ...");
m.onDirCreated = delegate(string path) { Monitor m;
writeln("Directory created: ", path); m.onDirCreated = delegate(string path) {
sync.createFolderItem(path); if (verbose) writeln("[M] Directory created: ", path);
sync.uploadDifferences(path); sync.createFolderItem(path);
}; sync.uploadDifferences(path);
m.onFileChanged = delegate(string path) { };
writeln("File changed: ", path); m.onFileChanged = delegate(string path) {
sync.uploadDifference2(path); if (verbose) writeln("[M] File changed: ", path);
}; sync.uploadDifference2(path);
m.onDelete = delegate(string path) { };
sync.deleteByPath(path); m.onDelete = delegate(string path) {
}; if (verbose) writeln("[M] Item deleted: ", path);
m.onMove = delegate(string from, string to) { sync.deleteByPath(path);
sync.moveItem(from, to); };
}; m.onMove = delegate(string from, string to) {
m.init(); if (verbose) writeln("[M] Item moved: ", from, " -> ", to);
string syncDir = cfg.get("sync_dir"); sync.moveItem(from, to);
chdir(syncDir); };
m.addRecursive("test"); m.init();
while (true) m.update(); string syncDir = cfg.get("sync_dir");
chdir(syncDir);
m.addRecursive("test");
while (true) m.update();
// TODO download changes
}
} }

View file

@ -1,6 +1,5 @@
module onedrive;
import std.datetime, std.json, std.net.curl, std.path, std.string, std.uni, std.uri; import std.datetime, std.json, std.net.curl, std.path, std.string, std.uni, std.uri;
import config;
private immutable { private immutable {
string authUrl = "https://login.live.com/oauth20_authorize.srf"; string authUrl = "https://login.live.com/oauth20_authorize.srf";
@ -30,18 +29,14 @@ final class OneDriveApi
private SysTime accessTokenExpiration; private SysTime accessTokenExpiration;
private HTTP http; private HTTP http;
void function(string) onRefreshToken; // called when a new refresh_token is received void delegate(string) onRefreshToken; // called when a new refresh_token is received
this(string clientId, string clientSecret) this(Config cfg, bool verbose)
{ {
this.clientId = clientId; this.clientId = cfg.get("client_id");
this.clientSecret = clientSecret; this.clientSecret = cfg.get("client_secret");
http = HTTP(); http = HTTP();
//debug http.verbose = true; http.verbose = verbose;
// HACK: prevent SIGPIPE
//import etc.c.signal, etc.c.curl;
//http.handle.set(CurlOption.nosignal, 0);
//signal(/*SIGPIPE*/ 13, /*SIG_IGN*/ cast(void function(int)) 1);
} }
~this() ~this()
@ -53,8 +48,8 @@ final class OneDriveApi
{ {
import std.stdio, std.regex; import std.stdio, std.regex;
string url = authUrl ~ "?client_id=" ~ clientId ~ "&scope=wl.offline_access onedrive.readwrite&response_type=code&redirect_url=" ~ redirectUrl; string url = authUrl ~ "?client_id=" ~ clientId ~ "&scope=wl.offline_access onedrive.readwrite&response_type=code&redirect_url=" ~ redirectUrl;
writeln("Authorize this app visiting:"); writeln("Authorize this app visiting:\n");
writeln(url); writeln(url, "\n");
while (true) { while (true) {
char[] response; char[] response;
@ -62,7 +57,7 @@ final class OneDriveApi
readln(response); readln(response);
auto c = matchFirst(response, r"(?:code=)(([\w\d]+-){4}[\w\d]+)"); auto c = matchFirst(response, r"(?:code=)(([\w\d]+-){4}[\w\d]+)");
if (!c.empty) { if (!c.empty) {
c.popFront(); // skip whole match c.popFront(); // skip the whole match
redeemToken(c.front); redeemToken(c.front);
break; break;
} }