summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules6
-rw-r--r--build.zig22
m---------lib/apple_pie0
m---------lib/sqlite0
-rw-r--r--src/main.zig131
-rw-r--r--src/store.zig130
-rw-r--r--ui/index.html16
-rw-r--r--ui/script.js56
-rw-r--r--ui/words.js501
9 files changed, 862 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..1d0b5a7
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "lib/apple_pie"]
+ path = lib/apple_pie
+ url = https://github.com/Luukdegram/apple_pie.git
+[submodule "lib/sqlite"]
+ path = lib/sqlite
+ url = https://github.com/leroycep/sqlite-zig
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..d73521e
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,22 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+ const target = b.standardTargetOptions(.{});
+
+ const mode = b.standardReleaseOptions();
+
+ const exe = b.addExecutable("accspy", "src/main.zig");
+ exe.setTarget(target);
+ exe.setBuildMode(mode);
+ exe.addPackagePath("apple_pie", "lib/apple_pie/src/apple_pie.zig");
+ //exe.addPackagePath("sqlite", "lib/sqlite/src/sqlite.zig");
+ //exe.linkSystemLibrary("sqlite3");
+ //exe.linkSystemLibrary("c");
+ exe.install();
+
+ const run_cmd = exe.run();
+ run_cmd.step.dependOn(b.getInstallStep());
+
+ const run_step = b.step("run", "Run the app");
+ run_step.dependOn(&run_cmd.step);
+}
diff --git a/lib/apple_pie b/lib/apple_pie
new file mode 160000
+Subproject c49b8cc4f13bc1618b20f618c435f4411d82c10
diff --git a/lib/sqlite b/lib/sqlite
new file mode 160000
+Subproject 28181e526281e3d19b8830bb0bebb8a19ca6cdd
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..08ff23e
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,131 @@
+const std = @import("std");
+const mem = std.mem;
+const json = std.json;
+const allocator = std.heap.page_allocator;
+
+const http = @import("apple_pie");
+const file_server = http.file_server;
+
+const Error = error{ BadRequest, MissingInfo };
+
+const store = @import("store.zig");
+
+pub fn main() anyerror!void {
+ // try file_server.init("ui/", allocator);
+ // defer file_server.deinit();
+ try http.server.listenAndServe(
+ allocator,
+ try std.net.Address.parseIp("127.0.0.1", 8080),
+ comptime Router(&[_]Route{
+ // .{
+ // .path = "/ui",
+ // .serveFn = file_server.serve,
+ //},
+ .{
+ .path = "/addurl",
+ .serveFn = addurl,
+ },
+ .{
+ .path = "/getlogs",
+ .serveFn = getlogs,
+ },
+ }),
+ );
+}
+
+const AddUrlRequest = struct {
+ username: []u8,
+ title: []u8,
+ url: []u8,
+ timestamp: i64,
+};
+
+const GetLogsRequest = struct {
+ username: []u8,
+ from: i64, // TODO implement
+};
+
+// TODO respond with appropriate error codes instead of returning error
+// removing Zig errors except for unrecoverable ones
+fn addurl(response: *http.Response, request: http.Request) !void {
+ std.debug.print("Is this working?\n", .{});
+ if (!mem.eql(u8, request.method, "POST")) {
+ try response.write("Try with POST");
+ return;
+ }
+
+ var parser = json.Parser.init(allocator, true);
+ defer parser.deinit();
+
+ var stream = json.TokenStream.init(request.body);
+ const parsedReq = try json.parse(AddUrlRequest, &stream, .{ .allocator = allocator });
+
+ const log = store.Log{
+ .url = parsedReq.url,
+ .title = parsedReq.title,
+ .timestamp = parsedReq.timestamp,
+ };
+
+ var logger = try store.Logger.init(allocator, parsedReq.username);
+
+ std.debug.print("Putting into database...\n", .{});
+
+ try logger.addURL(log);
+
+ std.debug.print("Successfully logged {}\n", .{log});
+
+ try response.write("Success\n");
+}
+
+fn getlogs(response: *http.Response, request: http.Request) !void {
+ std.debug.print("What.\n", .{});
+ if (!mem.eql(u8, request.method, "POST")) {
+ try response.write("Try with POST");
+ return;
+ }
+
+ //try response.headers.put("Access-Control-Allow-Origin", "*");
+ //try response.headers.put("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
+ //try response.headers.put("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept");
+
+ var parser = json.Parser.init(allocator, true);
+ defer parser.deinit();
+
+ var stream = json.TokenStream.init(request.body);
+ const parsedReq = try json.parse(GetLogsRequest, &stream, .{ .allocator = allocator });
+
+ var logger = try store.Logger.init(allocator, parsedReq.username);
+
+ const logs = try logger.getLogs();
+ var string = std.ArrayList(u8).init(allocator);
+ try json.stringify(logs, .{}, string.writer());
+
+ std.debug.print("Responding with: {}\n", .{string.items});
+ try response.write(string.items);
+}
+
+fn Router(comptime routes: []const Route) http.server.RequestHandler {
+ std.debug.assert(routes.len > 0);
+
+ return struct {
+ fn serve(response: *http.Response, request: http.Request) callconv(.Async) !void {
+ inline for (routes) |route| {
+ if (std.mem.eql(u8, request.url.path, route.path)) { // TODO fix this terrible sin with a better router
+ return route.serveFn(response, request);
+ }
+ }
+ }
+ }.serve;
+}
+
+const Route = struct {
+ path: []const u8,
+ serveFn: http.server.RequestHandler,
+
+ fn init(path: []const u8, serveFn: http.server.RequestHandler) Route {
+ return Route{
+ .path = path,
+ .serveFn = serveFn,
+ };
+ }
+};
diff --git a/src/store.zig b/src/store.zig
new file mode 100644
index 0000000..9d3fc6f
--- /dev/null
+++ b/src/store.zig
@@ -0,0 +1,130 @@
+const std = @import("std");
+const mem = std.mem;
+
+const default_allocator = std.heap.page_allocator;
+
+const logdir = "logs";
+
+pub const Log = struct {
+ url: []const u8,
+ title: []const u8,
+ timestamp: i64,
+};
+
+/// gets and retrieves data from activity logs
+/// TODO decide what to do when file is not found or contents are invalid
+pub const Logger = struct {
+ user: []const u8,
+ path: []u8,
+ file: std.fs.File,
+ allocator: *std.mem.Allocator,
+
+ pub fn init(allocator: *std.mem.Allocator, user: []const u8) !@This() {
+ var logfile = try std.fmt.allocPrint(allocator, "{}.csv", .{user});
+
+ var file = try std.fs.cwd().openFile(logfile[0..], .{ .write = true });
+
+ return @This(){
+ .user = user,
+ .path = logfile,
+ .file = file,
+ .allocator = allocator,
+ };
+ }
+
+ /// adds a URL to the logs
+ pub fn addURL(self: @This(), log: Log) !void {
+ try self.file.seekTo(try self.file.getEndPos());
+
+ try self.file.writer().print("{},{},{}\n", .{ log.timestamp, log.title, log.url });
+ }
+
+ pub fn getLogs(self: @This()) ![]Log {
+ var logs = std.ArrayList(Log).init(self.allocator);
+ var lines = std.ArrayList(u8).init(self.allocator);
+ // ^ - to provide a combination of lines that is not deallocated, allowing slices to remain
+
+ // read character by character to get line and then
+ // divide it by commas to determine each value
+ var line_num: usize = 0;
+ var line = std.ArrayList(u8).init(self.allocator);
+ var lines_i: usize = 0;
+ var part: u2 = 0; // can be 0, 1, or 2S
+ var chars: usize = 0;
+ try self.file.seekTo(0);
+ while (true) {
+ var char = self.file.reader().readByte() catch |_| break;
+
+ switch (char) {
+ else => {
+ _ = try line.append(char);
+ },
+ '\n' => {
+ var old_len = lines.items.len;
+ try lines.appendSlice(line.items);
+ var iter = mem.split(lines.items[old_len..], ",");
+ std.debug.print("Line: {}\n", .{lines.items[old_len..]});
+ var timestamp = iter.next() orelse continue;
+ std.debug.print("Time: {}\n", .{try std.fmt.parseInt(i64, timestamp, 10)});
+ var log = Log{
+ .timestamp = try std.fmt.parseInt(i64, (timestamp), 10),
+ .title = iter.next() orelse continue,
+ .url = iter.next() orelse continue,
+ };
+ try logs.append(log);
+
+ chars += line.items.len + 1;
+ try self.file.seekTo(chars);
+ line_num += 1;
+
+ line.deinit();
+ line = std.ArrayList(u8).init(self.allocator);
+ },
+ }
+ }
+
+ return logs.items;
+ }
+
+ /// returns all logs from a certain date
+ pub fn getFrom(self: @This(), logs: []Log, timestamp: i64) ![]Log {
+ var newLogs = std.ArrayList(Log).init(self.allocator);
+ for (logs) |log| {
+ if (log.timestamp >= timestamp) {
+ std.debug.print("Adding {}\n", .{log});
+ try newLogs.append(log);
+ }
+ }
+ return newLogs.items;
+ }
+
+ pub fn deinit(self: @This()) !void {
+ try self.file.close();
+ }
+};
+
+test "add a URL" {
+ var logger = try Logger.init(default_allocator, "j");
+ try logger.addURL(Log{
+ .url = "https://josias.dev/",
+ .title = "The blog of josias",
+ .timestamp = std.time.milliTimestamp(),
+ });
+}
+
+test "get logs" {
+ var logger = try Logger.init(default_allocator, "j");
+ const logs = try logger.getLogs();
+ for (logs) |log| {
+ std.debug.print("Log: {}\n", .{log});
+ }
+}
+
+test "get logs past a certain date" {
+ var logger = try Logger.init(default_allocator, "j");
+ const logs = try logger.getLogs();
+ const newLogs = logger.getFrom(logs, 159760468400);
+ for (logs) |log| {
+ std.debug.print("Log: {}\n", .{log});
+ }
+}
diff --git a/ui/index.html b/ui/index.html
new file mode 100644
index 0000000..b2419c2
--- /dev/null
+++ b/ui/index.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>AccSpy</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+</head>
+<body>
+ <input id="username" placeholder="username" /><button id="getLogsButton">Search</button>
+
+ <h1>Logs for <span id="username"></span></h1>
+
+ <table id="logs"></table>
+
+ <script src="words.js"></script>
+ <script src="script.js"></script>
+</body>
diff --git a/ui/script.js b/ui/script.js
new file mode 100644
index 0000000..1183f49
--- /dev/null
+++ b/ui/script.js
@@ -0,0 +1,56 @@
+const APIURL = "http://localhost:8080";
+
+async function getLogs(username) {
+ const data = {
+ username: username,
+ from: 1,
+ };
+ console.log("Sending " + JSON.stringify(data));
+
+ const response = await fetch(APIURL + "/getlogs",
+ {
+ method: "POST",
+ cors: "no-cors",
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ }
+ );
+ return response.json();
+}
+
+document.getElementById("getLogsButton").addEventListener("click", async function () {
+ const username = document.getElementById("username").value;
+
+ let logs = await getLogs(username);
+ logs = logs.reverse(); // reverse with newest first
+
+ let el = document.getElementById("logs");
+ el.innerHTML = "";
+
+ for (i in logs) {
+ console.log(logs[i].title);
+ let log = document.createElement("tr");
+ log.className = "normal";
+ let d = new Date(logs[i].timestamp);
+ log.innerHTML = "<th>" + d.toISOString() + "</th><th>" + logs[i].title + "</th><th>" + logs[i].url + "</th>";
+
+ // if the title or url contains questionable
+ // words, color the text red
+ for (x in wordListArray) {
+ if (logs[i].title.toLowerCase().includes(wordListArray[x])
+ || logs[i].url.toLowerCase().includes(wordListArray[x])
+ ) {
+ log.style.color = "red";
+ }
+ }
+
+ el.appendChild(log);
+ }
+});
+
+
+
+
+
diff --git a/ui/words.js b/ui/words.js
new file mode 100644
index 0000000..7b5d882
--- /dev/null
+++ b/ui/words.js
@@ -0,0 +1,501 @@
+
+// Warning! Word list below. Contains profanity and other offensive language.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+const wordList = `2g1c
+2 girls 1 cup
+acrotomophilia
+alabama hot pocket
+alaskan pipeline
+anal
+anilingus
+anus
+apeshit
+arsehole
+ass
+asshole
+assmunch
+auto erotic
+autoerotic
+babeland
+baby batter
+baby juice
+ball gag
+ball gravy
+ball kicking
+ball licking
+ball sack
+ball sucking
+bangbros
+bangbus
+bareback
+barely legal
+barenaked
+bastard
+bastardo
+bastinado
+bbw
+bdsm
+beaner
+beaners
+beaver cleaver
+beaver lips
+beastiality
+bestiality
+big black
+big breasts
+big knockers
+big tits
+bimbos
+birdlock
+bitch
+bitches
+black cock
+blonde action
+blonde on blonde action
+blowjob
+blow job
+blow your load
+blue waffle
+blumpkin
+bollocks
+bondage
+boner
+boob
+boobs
+booty call
+brown showers
+brunette action
+bukkake
+bulldyke
+bullet vibe
+bullshit
+bung hole
+bunghole
+busty
+butt
+buttcheeks
+butthole
+camel toe
+camgirl
+camslut
+camwhore
+carpet muncher
+carpetmuncher
+chocolate rosebuds
+cialis
+circlejerk
+cleveland steamer
+clit
+clitoris
+clover clamps
+clusterfuck
+cock
+cocks
+coprolagnia
+coprophilia
+cornhole
+coon
+coons
+creampie
+cum
+cumming
+cumshot
+cumshots
+cunnilingus
+cunt
+darkie
+date rape
+daterape
+deep throat
+deepthroat
+dendrophilia
+dick
+dildo
+dingleberry
+dingleberries
+dirty pillows
+dirty sanchez
+doggie style
+doggiestyle
+doggy style
+doggystyle
+dog style
+dolcett
+domination
+dominatrix
+dommes
+donkey punch
+double dong
+double penetration
+dp action
+dry hump
+dvda
+eat my ass
+ecchi
+ejaculation
+erotic
+erotism
+escort
+eunuch
+fag
+faggot
+fecal
+felch
+fellatio
+feltch
+female squirting
+femdom
+figging
+fingerbang
+fingering
+fisting
+foot fetish
+footjob
+frotting
+fuck
+fuck buttons
+fuckin
+fucking
+fucktards
+fudge packer
+fudgepacker
+futanari
+gangbang
+gang bang
+gay sex
+genitals
+giant cock
+girl on
+girl on top
+girls gone wild
+goatcx
+goatse
+god damn
+gokkun
+golden shower
+goodpoop
+goo girl
+goregasm
+grope
+group sex
+g-spot
+guro
+hand job
+handjob
+hard core
+hardcore
+hentai
+homoerotic
+honkey
+hooker
+horny
+hot carl
+hot chick
+how to kill
+how to murder
+huge fat
+humping
+incest
+intercourse
+jack off
+jail bait
+jailbait
+jelly donut
+jerk off
+jigaboo
+jiggaboo
+jiggerboo
+jizz
+juggs
+kike
+kinbaku
+kinkster
+kinky
+knobbing
+leather restraint
+leather straight jacket
+lemon party
+livesex
+lolita
+lovemaking
+make me come
+male squirting
+masturbate
+masturbating
+masturbation
+menage a trois
+milf
+missionary position
+mong
+motherfucker
+mound of venus
+mr hands
+muff diver
+muffdiving
+nambla
+nawashi
+negro
+neonazi
+nigga
+nigger
+nig nog
+nimphomania
+nipple
+nipples
+nsfw
+nsfw images
+nude
+nudity
+nutten
+nympho
+nymphomania
+octopussy
+omorashi
+one cup two girls
+one guy one jar
+orgasm
+orgy
+paedophile
+paki
+panties
+panty
+pedobear
+pedophile
+pegging
+penis
+phone sex
+piece of shit
+pikey
+pissing
+piss pig
+pisspig
+playboy
+pleasure chest
+pole smoker
+ponyplay
+poof
+poon
+poontang
+punany
+poop chute
+poopchute
+porn
+porno
+pornography
+prince albert piercing
+pthc
+pubes
+pussy
+queaf
+queef
+quim
+raghead
+raging boner
+rape
+raping
+rapist
+rectum
+reverse cowgirl
+rimjob
+rimming
+rosy palm
+rosy palm and her 5 sisters
+rusty trombone
+sadism
+santorum
+scat
+schlong
+scissoring
+semen
+sex
+sexcam
+sexo
+sexy
+sexual
+sexually
+sexuality
+shaved beaver
+shaved pussy
+shemale
+shibari
+shit
+shitblimp
+shitty
+shota
+shrimping
+skeet
+slanteye
+slut
+s&m
+smut
+snatch
+snowballing
+sodomize
+sodomy
+spastic
+spic
+splooge
+splooge moose
+spooge
+spread legs
+spunk
+strap on
+strapon
+strappado
+strip club
+style doggy
+suck
+sucks
+suicide girls
+sultry women
+swastika
+swinger
+tainted love
+taste my
+tea bagging
+threesome
+throating
+thumbzilla
+tied up
+tight white
+tit
+tits
+titties
+titty
+tongue in a
+topless
+tosser
+towelhead
+tranny
+tribadism
+tub girl
+tubgirl
+tushy
+twat
+twink
+twinkie
+two girls one cup
+undressing
+upskirt
+urethra play
+urophilia
+vagina
+venus mound
+viagra
+vibrator
+violet wand
+vorarephilia
+voyeur
+voyeurweb
+voyuer
+vulva
+wank
+wetback
+wet dream
+white power
+whore
+worldsex
+wrapping men
+wrinkled starfish
+xx
+xxx
+yaoi
+yellow showers
+yiffy
+zoophilia
+🖕`
+
+const wordListArray = wordList.split("\n");