diff --git a/assets/main.db b/assets/main.db index 92bb7e9..ef65588 100644 Binary files a/assets/main.db and b/assets/main.db differ diff --git a/config.yml b/config.yml index 8a3dd08..867b9dc 100644 --- a/config.yml +++ b/config.yml @@ -10,3 +10,5 @@ log: log_save: true leveldb: data_path: ./data/honoka-chan.db +cdn: + cdn_url: http://192.168.1.12:8081/static diff --git a/config/yaml.go b/config/yaml.go index 73cc527..1b95bc9 100644 --- a/config/yaml.go +++ b/config/yaml.go @@ -18,6 +18,7 @@ type AppConfigs struct { Server ServerConfigs `yaml:"server"` Log LogConfigs `yaml:"log"` LevelDb LevelDbConfigs `yaml:"leveldb"` + Cdn CdnConfigs `yaml:"cdn"` } type ServerConfigs struct { @@ -37,6 +38,10 @@ type LevelDbConfigs struct { DataPath string `yaml:"data_path"` } +type CdnConfigs struct { + CdnUrl string `yaml:"cdn_url"` +} + func DefaultConfigs() *AppConfigs { return &AppConfigs{ AppName: "LL! SIF Private Server", @@ -54,6 +59,9 @@ func DefaultConfigs() *AppConfigs { LevelDb: LevelDbConfigs{ DataPath: "./data/honoka-chan.db", }, + Cdn: CdnConfigs{ + CdnUrl: "", + }, } } diff --git a/handler/download.go b/handler/download.go index e0e51ec..7fc215e 100644 --- a/handler/download.go +++ b/handler/download.go @@ -1,25 +1,28 @@ package handler import ( + "database/sql" "encoding/base64" "encoding/json" "fmt" "honoka-chan/database" "honoka-chan/encrypt" + "honoka-chan/model" "honoka-chan/utils" "net/http" "time" "github.com/gin-gonic/gin" + _ "github.com/mattn/go-sqlite3" ) -type DownloadResp struct { - ResponseData []interface{} `json:"response_data"` - ReleaseInfo []interface{} `json:"release_info"` - StatusCode int `json:"status_code"` -} +func DownloadAdditionalHandler(ctx *gin.Context) { + db, err := sql.Open("sqlite3", "assets/main.db") + if err != nil { + panic(err) + } + defer db.Close() -func DownloadBatchHandler(ctx *gin.Context) { reqTime := time.Now().Unix() authorizeStr := ctx.Request.Header["Authorize"] @@ -51,8 +54,121 @@ func DownloadBatchHandler(ctx *gin.Context) { newAuthorizeStr := fmt.Sprintf("consumerKey=lovelive_test&timeStamp=%d&version=1.1&token=%s&nonce=%d&user_id=%s&requestTimeStamp=%d", respTime, authToken, nonce, userId[0], reqTime) // fmt.Println(newAuthorizeStr) - batchResp := DownloadResp{ - ResponseData: []interface{}{}, + downloadReq := model.AdditionalReq{} + if err := json.Unmarshal([]byte(ctx.PostForm("request_data")), &downloadReq); err != nil { + panic(err) + } + pkgList := []model.AdditionalResult{} + if CdnUrl != "" { + pkgType, pkgId := downloadReq.PackageType, downloadReq.PackageID + stmt, err := db.Prepare("SELECT pkg_order,pkg_size FROM download_db WHERE pkg_type = ? AND pkg_id = ? ORDER BY pkg_order ASC") + if err != nil { + panic(err) + } + rows, err := stmt.Query(pkgType, pkgId) + if err != nil { + panic(err) + } + for rows.Next() { + var pkgOrder, pkgSize int + err = rows.Scan(&pkgOrder, &pkgSize) + if err != nil { + panic(err) + } + pkgList = append(pkgList, model.AdditionalResult{ + Size: pkgSize, + URL: fmt.Sprintf("%s/%d_%d_%d.zip", CdnUrl, pkgType, pkgId, pkgOrder), + }) + } + } + + addResp := model.AdditionalResp{ + ResponseData: pkgList, + ReleaseInfo: []interface{}{}, + StatusCode: 200, + } + resp, err := json.Marshal(addResp) + if err != nil { + panic(err) + } + // fmt.Println(string(resp)) + xms := encrypt.RSA_Sign_SHA1(resp, "privatekey.pem") + xms64 := base64.RawStdEncoding.EncodeToString(xms) + + ctx.Header("user_id", userId[0]) + ctx.Header("authorize", newAuthorizeStr) + ctx.Header("X-Message-Sign", xms64) + ctx.String(http.StatusOK, string(resp)) +} + +func DownloadBatchHandler(ctx *gin.Context) { + db, err := sql.Open("sqlite3", "assets/main.db") + if err != nil { + panic(err) + } + defer db.Close() + + reqTime := time.Now().Unix() + + authorizeStr := ctx.Request.Header["Authorize"] + authToken, err := utils.GetAuthorizeToken(authorizeStr) + if err != nil { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + userId := ctx.Request.Header[http.CanonicalHeaderKey("User-ID")] + if len(userId) == 0 { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + + if !database.MatchTokenUid(authToken, userId[0]) { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + + nonce, err := utils.GetAuthorizeNonce(authorizeStr) + if err != nil { + fmt.Println(err) + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + nonce++ + + respTime := time.Now().Unix() + newAuthorizeStr := fmt.Sprintf("consumerKey=lovelive_test&timeStamp=%d&version=1.1&token=%s&nonce=%d&user_id=%s&requestTimeStamp=%d", respTime, authToken, nonce, userId[0], reqTime) + // fmt.Println(newAuthorizeStr) + + downloadReq := model.BatchReq{} + if err := json.Unmarshal([]byte(ctx.PostForm("request_data")), &downloadReq); err != nil { + panic(err) + } + pkgList := []model.BatchResult{} + if downloadReq.ClientVersion == PackageVersion && CdnUrl != "" { + pkgType := downloadReq.PackageType + stmt, err := db.Prepare("SELECT pkg_id,pkg_order,pkg_size FROM download_db WHERE pkg_type = ? ORDER BY pkg_id ASC, pkg_order ASC") + if err != nil { + panic(err) + } + rows, err := stmt.Query(pkgType) + if err != nil { + panic(err) + } + for rows.Next() { + var pkgId, pkgOrder, pkgSize int + err = rows.Scan(&pkgId, &pkgOrder, &pkgSize) + if err != nil { + panic(err) + } + pkgList = append(pkgList, model.BatchResult{ + Size: pkgSize, + URL: fmt.Sprintf("%s/%d_%d_%d.zip", CdnUrl, pkgType, pkgId, pkgOrder), + }) + } + } + + batchResp := model.BatchResp{ + ResponseData: pkgList, ReleaseInfo: []interface{}{}, StatusCode: 200, } @@ -60,6 +176,89 @@ func DownloadBatchHandler(ctx *gin.Context) { if err != nil { panic(err) } + // fmt.Println(string(resp)) + xms := encrypt.RSA_Sign_SHA1(resp, "privatekey.pem") + xms64 := base64.RawStdEncoding.EncodeToString(xms) + + ctx.Header("user_id", userId[0]) + ctx.Header("authorize", newAuthorizeStr) + ctx.Header("X-Message-Sign", xms64) + ctx.String(http.StatusOK, string(resp)) +} + +func DownloadUpdateHandler(ctx *gin.Context) { + db, err := sql.Open("sqlite3", "assets/main.db") + if err != nil { + panic(err) + } + defer db.Close() + + reqTime := time.Now().Unix() + + authorizeStr := ctx.Request.Header["Authorize"] + authToken, err := utils.GetAuthorizeToken(authorizeStr) + if err != nil { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + userId := ctx.Request.Header[http.CanonicalHeaderKey("User-ID")] + if len(userId) == 0 { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + + if !database.MatchTokenUid(authToken, userId[0]) { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + + nonce, err := utils.GetAuthorizeNonce(authorizeStr) + if err != nil { + fmt.Println(err) + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + nonce++ + + respTime := time.Now().Unix() + newAuthorizeStr := fmt.Sprintf("consumerKey=lovelive_test&timeStamp=%d&version=1.1&token=%s&nonce=%d&user_id=%s&requestTimeStamp=%d", respTime, authToken, nonce, userId[0], reqTime) + // fmt.Println(newAuthorizeStr) + + downloadReq := model.UpdateReq{} + if err := json.Unmarshal([]byte(ctx.PostForm("request_data")), &downloadReq); err != nil { + panic(err) + } + pkgList := []model.UpdateResult{} + if downloadReq.ExternalVersion != PackageVersion && CdnUrl != "" { + pkgType := 99 + rows, err := db.Query("SELECT pkg_id,pkg_order,pkg_size FROM download_db WHERE pkg_type = 99 ORDER BY pkg_id ASC, pkg_order ASC") + if err != nil { + panic(err) + } + for rows.Next() { + var pkgId, pkgOrder, pkgSize int + err = rows.Scan(&pkgId, &pkgOrder, &pkgSize) + if err != nil { + panic(err) + } + pkgList = append(pkgList, model.UpdateResult{ + Size: pkgSize, + URL: fmt.Sprintf("%s/%d_%d_%d.zip", CdnUrl, pkgType, pkgId, pkgOrder), + Version: PackageVersion, + }) + } + } + + updateResp := model.UpdateResp{ + ResponseData: pkgList, + ReleaseInfo: []interface{}{}, + StatusCode: 200, + } + resp, err := json.Marshal(updateResp) + if err != nil { + panic(err) + } + // fmt.Println(string(resp)) xms := encrypt.RSA_Sign_SHA1(resp, "privatekey.pem") xms64 := base64.RawStdEncoding.EncodeToString(xms) @@ -101,7 +300,7 @@ func DownloadEventHandler(ctx *gin.Context) { newAuthorizeStr := fmt.Sprintf("consumerKey=lovelive_test&timeStamp=%d&version=1.1&token=%s&nonce=%d&user_id=%s&requestTimeStamp=%d", respTime, authToken, nonce, userId[0], reqTime) // fmt.Println(newAuthorizeStr) - eventResp := DownloadResp{ + eventResp := model.EventResp{ ResponseData: []interface{}{}, ReleaseInfo: []interface{}{}, StatusCode: 200, diff --git a/handler/global.go b/handler/global.go index dd5e944..75f5dc1 100644 --- a/handler/global.go +++ b/handler/global.go @@ -1,5 +1,15 @@ package handler +import "honoka-chan/config" + var ( - nonce = 0 + nonce = 0 + PackageVersion = "97.4.6" + CdnUrl string ) + +func init() { + if config.Conf.Cdn.CdnUrl != "" { + CdnUrl = config.Conf.Cdn.CdnUrl + } +} diff --git a/handler/multiunit.go b/handler/multiunit.go new file mode 100644 index 0000000..195b1f8 --- /dev/null +++ b/handler/multiunit.go @@ -0,0 +1,98 @@ +package handler + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "honoka-chan/config" + "honoka-chan/database" + "honoka-chan/encrypt" + "honoka-chan/utils" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +type MultiUnitStartUpResp struct { + ResponseData MultiUnitStartUpData `json:"response_data"` + ReleaseInfo []interface{} `json:"release_info"` + StatusCode int `json:"status_code"` +} + +type MultiUnitStartUpData struct { + MultiUnitScenarioID int `json:"multi_unit_scenario_id"` + ScenarioAdjustment int `json:"scenario_adjustment"` + ServerTimestamp int64 `json:"server_timestamp"` +} + +type MultiUnitStartUpReq struct { + Module string `json:"module"` + Action string `json:"action"` + TimeStamp int `json:"timeStamp"` + Mgd int `json:"mgd"` + MultiUnitScenarioID int `json:"multi_unit_scenario_id"` + CommandNum string `json:"commandNum"` +} + +func MultiUnitStartUpHandler(ctx *gin.Context) { + reqTime := time.Now().Unix() + + startReq := MultiUnitStartUpReq{} + err := json.Unmarshal([]byte(ctx.PostForm("request_data")), &startReq) + if err != nil { + panic(err) + } + + authorizeStr := ctx.Request.Header["Authorize"] + authToken, err := utils.GetAuthorizeToken(authorizeStr) + if err != nil { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + userId := ctx.Request.Header[http.CanonicalHeaderKey("User-ID")] + if len(userId) == 0 { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + + if !database.MatchTokenUid(authToken, userId[0]) { + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + + nonce, err := utils.GetAuthorizeNonce(authorizeStr) + if err != nil { + fmt.Println(err) + ctx.String(http.StatusForbidden, "Fuck you!") + return + } + nonce++ + + respTime := time.Now().Unix() + newAuthorizeStr := fmt.Sprintf("consumerKey=lovelive_test&timeStamp=%d&version=1.1&token=%s&nonce=%d&user_id=%s&requestTimeStamp=%d", respTime, authToken, nonce, userId[0], reqTime) + // fmt.Println(newAuthorizeStr) + + startResp := MultiUnitStartUpResp{ + ResponseData: MultiUnitStartUpData{ + MultiUnitScenarioID: startReq.MultiUnitScenarioID, + ScenarioAdjustment: 50, + ServerTimestamp: time.Now().Unix(), + }, + ReleaseInfo: []interface{}{}, + StatusCode: 200, + } + resp, err := json.Marshal(startResp) + if err != nil { + panic(err) + } + xms := encrypt.RSA_Sign_SHA1(resp, "privatekey.pem") + xms64 := base64.RawStdEncoding.EncodeToString(xms) + + ctx.Header("Server-Version", config.Conf.Server.VersionNumber) + ctx.Header("user_id", userId[0]) + ctx.Header("authorize", newAuthorizeStr) + ctx.Header("X-Message-Sign", xms64) + + ctx.String(http.StatusOK, string(resp)) +} diff --git a/main.go b/main.go index 89012f1..288bc1b 100644 --- a/main.go +++ b/main.go @@ -54,7 +54,9 @@ func main() { m.POST("/gdpr/get", handler.GdprHandler) m.POST("/personalnotice/get", handler.PersonalNoticeHandler) m.POST("/tos/tosCheck", handler.TosCheckHandler) + m.POST("/download/additional", handler.DownloadAdditionalHandler) m.POST("/download/batch", handler.DownloadBatchHandler) + m.POST("/download/update", handler.DownloadUpdateHandler) m.POST("/download/event", handler.DownloadEventHandler) m.POST("/lbonus/execute", handler.LBonusExecuteHandler) m.POST("/api", handler.ApiHandler) @@ -78,6 +80,7 @@ func main() { m.POST("/notice/noticeFriendVariety", handler.NoticeFriendVarietyHandler) m.POST("/notice/noticeFriendGreeting", handler.NoticeFriendGreetingHandler) m.POST("/notice/noticeUserGreetingHistory", handler.NoticeUserGreetingHandler) + m.POST("/multiunit/scenarioStartup", handler.MultiUnitStartUpHandler) } r.GET("/webview.php/announce/index", handler.AnnounceIndexHandler) // Server APIs diff --git a/model/download.go b/model/download.go new file mode 100644 index 0000000..e58c102 --- /dev/null +++ b/model/download.go @@ -0,0 +1,71 @@ +package model + +type AdditionalReq struct { + Module string `json:"module"` + Mgd int `json:"mgd"` + Action string `json:"action"` + TimeStamp int `json:"timeStamp"` + PackageID int `json:"package_id"` + TargetOs string `json:"target_os"` + PackageType int `json:"package_type"` + CommandNum string `json:"commandNum"` +} + +type AdditionalResult struct { + Size int `json:"size"` + URL string `json:"url"` +} + +type AdditionalResp struct { + ResponseData []AdditionalResult `json:"response_data"` + ReleaseInfo []interface{} `json:"release_info"` + StatusCode int `json:"status_code"` +} + +type BatchReq struct { + ClientVersion string `json:"client_version"` + Os string `json:"os"` + PackageType int `json:"package_type"` + ExcludedPackageIds []interface{} `json:"excluded_package_ids"` + CommandNum string `json:"commandNum"` +} + +type BatchResult struct { + Size int `json:"size"` + URL string `json:"url"` +} + +type BatchResp struct { + ResponseData []BatchResult `json:"response_data"` + ReleaseInfo []interface{} `json:"release_info"` + StatusCode int `json:"status_code"` +} + +type UpdateReq struct { + Module string `json:"module"` + TargetOs string `json:"target_os"` + InstallVersion string `json:"install_version"` + TimeStamp int `json:"timeStamp"` + Action string `json:"action"` + PackageList []interface{} `json:"package_list"` + CommandNum string `json:"commandNum"` + ExternalVersion string `json:"external_version"` +} + +type UpdateResult struct { + Size int `json:"size"` + URL string `json:"url"` + Version string `json:"version"` +} + +type UpdateResp struct { + ResponseData []UpdateResult `json:"response_data"` + ReleaseInfo []interface{} `json:"release_info"` + StatusCode int `json:"status_code"` +} + +type EventResp struct { + ResponseData []interface{} `json:"response_data"` + ReleaseInfo []interface{} `json:"release_info"` + StatusCode int `json:"status_code"` +} diff --git a/tools/download_db.go b/tools/download_db.go new file mode 100644 index 0000000..863a551 --- /dev/null +++ b/tools/download_db.go @@ -0,0 +1,62 @@ +package tools + +import ( + "database/sql" + "fmt" + "os" + "strings" + + _ "github.com/mattn/go-sqlite3" +) + +func GenDownloadDb() { + // Create table + // sql := `CREATE TABLE "main"."download_db" ( + // "id" integer NOT NULL, + // "pkg_type" integer, + // "pkg_id" integer, + // "pkg_order" integer, + // "pkg_size" integer, + // PRIMARY KEY ("id") + // )` + db, err := sql.Open("sqlite3", "assets/main.db") + if err != nil { + panic(err) + } + defer db.Close() + + fileLists, err := os.ReadDir("F:/sif_dl/list_CN_Android") + if err != nil { + panic(err) + } + for _, v := range fileLists { + if v.IsDir() { + panic(err) + } + fileList := "F:/sif_dl/list_CN_Android/" + v.Name() + fileStat, err := os.Stat(fileList) + if err != nil { + panic(err) + } + pkgSize := fileStat.Size() + fileInfo := strings.Split(strings.ReplaceAll(v.Name(), ".zip", ""), "_") + pkgType, pkgId, pkgOrder := fileInfo[0], fileInfo[1], fileInfo[2] + fmt.Printf("%s - %s - %s - %d\n", pkgType, pkgId, pkgOrder, pkgSize) + + stmt, err := db.Prepare("INSERT INTO download_db(pkg_type,pkg_id,pkg_order,pkg_size) VALUES (?,?,?,?)") + if err != nil { + panic(err) + } + + res, err := stmt.Exec(pkgType, pkgId, pkgOrder, pkgSize) + if err != nil { + panic(err) + } + + id, err := res.LastInsertId() + if err != nil { + panic(err) + } + fmt.Println("LastInsertId:", id) + } +} diff --git a/tools/tools.go b/tools/tools.go index cf60a49..9050da8 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -9,4 +9,5 @@ func init() { LoadApi3Data("assets/api3.json") // ListUnitData() // go SyncNotesList() + // GenDownloadDb() }