data.go 5.76 KB
package main

import (
	"context"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/jmoiron/sqlx"
	"github.com/labstack/echo/v4"
	"github.com/olivere/elastic/v7"

	_ "github.com/mattn/go-sqlite3"
)

type Call struct {
	ID       int       `json:"id" db:"id"`
	Type     int       `json:"type" db:"type"`
	Name     *string   `json:"name" db:"name"`
	Number   int       `json:"number" db:"number"`
	Duration int       `json:"duration" db:"duration"`
	Date     time.Time `json:"date" db:"date"`
}

func (app *App) GetCalls(c echo.Context) error {
	calls := []Call{}

	query := `SELECT * FROM calls WHERE extraction_no=?`
	app.db.Unsafe().Select(&calls, query, c.Param("no"))

	return c.JSON(http.StatusOK, calls)
}

type CallStats struct {
	Number   string  `json:"number" db:"number"`
	Name     *string `json:"name" db:"name"`
	Incoming int     `json:"incoming" db:"incoming"`
	Outgoing int     `json:"outgoing" db:"outgoing"`
	Duration int     `json:"duration" db:"duration"`
}

func (app *App) GetCallsAnalyses(c echo.Context) error {
	calls := []CallStats{}

	query := `SELECT number, name,
		(SELECT COUNT(1) FROM calls s WHERE s.extraction_no=c.extraction_no AND s.number=c.number AND s.type=1) incoming,
		(SELECT COUNT(1) FROM calls s WHERE s.extraction_no=c.extraction_no AND s.number=c.number AND s.type=2) outgoing,
		SUM(duration) duration
	FROM calls c WHERE extraction_no=? GROUP BY number ORDER BY duration DESC`
	app.db.Select(&calls, query, c.Param("no"))

	return c.JSON(http.StatusOK, calls)
}

type AppInfo struct {
	PackageName    string    `json:"package_name" db:"package_name"`
	Name           string    `json:"name" db:"name"`
	Version        string    `json:"version" db:"version"`
	WifiUsage      int       `json:"wifi_usage" db:"wifi_usage"`
	CellularUsage  int       `json:"cellular_usage" db:"cellular_usage"`
	LastUsed       time.Time `json:"last_used" db:"last_used"`
	ForegroundTime int64     `json:"foreground_time" db:"foreground_time"`
}

func (app *App) GetAppsAnalyses(c echo.Context) error {
	apps := []AppInfo{}

	query := `SELECT * FROM apps WHERE extraction_no=? ORDER BY foreground_time DESC LIMIT 0, 100`
	app.db.Unsafe().Select(&apps, query, c.Param("no"))

	return c.JSON(http.StatusOK, apps)
}

type Message struct {
	ID      int       `json:"id" db:"id"`
	Type    int       `json:"type" db:"type"`
	Address string    `json:"address" db:"address"`
	Body    string    `json:"body" db:"body"`
	Date    time.Time `json:"date" db:"date"`
}

func (app *App) GetMessages(c echo.Context) error {
	messages := []Message{}

	no := c.Param("no")

	q := c.QueryParam("q")
	if len(q) > 0 {
		res, err := app.es.Search("messages-" + no).
			Query(elastic.NewMatchQuery("content", q)).
			Do(context.Background())

		if err != nil {
			return err
		}

		for _, hit := range res.Hits.Hits {
			message := Message{}

			app.db.Unsafe().Get(
				&message,
				"SELECT * FROM messages WHERE extraction_no=? AND `id`=?",
				no, hit.Id,
			)

			messages = append(messages, message)
		}
	} else {
		app.db.Unsafe().Select(&messages, `SELECT * FROM messages WHERE extraction_no=?`, no)
	}

	return c.JSON(http.StatusOK, messages)
}

type MessageStats struct {
	Address string `json:"address" db:"address"`
	Receive int    `json:"receive" db:"receive"`
	Send    int    `json:"send" db:"send"`
}

func (app *App) GetMessagesAnalyses(c echo.Context) error {
	messages := []MessageStats{}

	query := `SELECT address,
		(SELECT COUNT(1) FROM messages m WHERE m.extraction_no=s.extraction_no AND m.address=s.address AND m.type=1) receive,
		(SELECT COUNT(1) FROM messages m WHERE m.extraction_no=s.extraction_no AND m.address=s.address AND m.type=2) send
	FROM messages s WHERE extraction_no=? GROUP BY address ORDER BY receive + send DESC`
	app.db.Select(&messages, query, c.Param("no"))

	return c.JSON(http.StatusOK, messages)
}

type Process struct {
	UID   string `json:"uid" db:"UID"`
	PID   int    `json:"pid" db:"PID"`
	PPID  int    `json:"ppid" db:"PPID"`
	STIME string `json:"stime" db:"STIME"`
	TIME  string `json:"time" db:"TIME"`
	CMD   string `json:"cmd" db:"CMD"`
}

func (app *App) GetProcesses(c echo.Context) error {
	processes := []Process{}
	db, err := sqlx.Connect("sqlite3", fmt.Sprintf("data/1/%s", c.Param("file")))
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Could not open db file")
	}
	defer db.Close()

	query := `SELECT UID, CAST(PID AS INTEGER) PID, CAST(PPID AS INTEGER) PPID, STIME, TIME, CMD FROM process WHERE UID LIKE 'u%' ORDER BY TIME DESC`
	db.Select(&processes, query)

	return c.JSON(http.StatusOK, processes)
}

type Alarm struct {
	ID      string         `json:"id"`
	When    time.Time      `json:"when"`
	History []AlarmHistory `json:"history"`
}

type AlarmHistory struct {
	Type string    `json:"type"`
	When time.Time `json:"when"`
}

func (app *App) GetAlarms(c echo.Context) error {
	db, err := sqlx.Connect("sqlite3", fmt.Sprintf("data/1/%s", c.Param("file")))
	if err != nil {
		return echo.NewHTTPError(http.StatusInternalServerError, "Could not open db file")
	}
	defer db.Close()

	alarms := map[string]Alarm{}
	rows, _ := db.Queryx("SELECT * FROM alarm ORDER BY TIME")

	for rows.Next() {
		var tm string
		var typ string
		var detail string

		rows.Scan(&tm, &typ, &detail)

		detail = detail[strings.Index(detail, "{")+1 : strings.Index(detail, "}")]
		s := strings.Split(detail, " ")
		timestamp, _ := strconv.ParseInt(s[4], 10, 64)
		timestamp /= 1000

		if _, ok := alarms[s[0]]; !ok {
			alarms[s[0]] = Alarm{ID: s[0], When: time.Unix(timestamp, 0)}
		}

		when, _ := time.Parse("2006-01-02 15:04:05", tm)
		alarm := alarms[s[0]]
		alarm.History = append(alarms[s[0]].History, AlarmHistory{
			Type: typ,
			When: when,
		})
		alarms[s[0]] = alarm
	}

	results := []Alarm{}
	for _, v := range alarms {
		results = append(results, v)
	}

	return c.JSON(http.StatusOK, results)
}