package handler import ( "encoding/base64" "encoding/json" "fmt" "honoka-chan/config" "honoka-chan/db" "honoka-chan/encrypt" "honoka-chan/tools" "honoka-chan/utils" "io" "net/http" "net/url" "strconv" "strings" "time" "github.com/forgoer/openssl" "github.com/gin-gonic/gin" ) type LoginResp struct { Activation int `json:"activation"` Autokey string `json:"autokey"` CaptchaParams string `json:"captchaParams"` CheckCodeGUID string `json:"checkCodeGuid"` CheckCodeURL string `json:"checkCodeUrl"` HasExtendAccs int `json:"hasExtendAccs"` HasRealInfo int `json:"has_realInfo"` ImagecodeType int `json:"imagecodeType"` IsNewUser int `json:"isNewUser"` Message string `json:"message"` NextAction int `json:"nextAction"` PromptMsg string `json:"prompt_msg"` RealInfoNotification string `json:"realInfoNotification"` RealInfoForce int `json:"realInfo_force"` RealInfoForcePay int `json:"realInfo_force_pay"` RealInfoStatus int `json:"realInfo_status"` RealInfoStatusPay int `json:"realInfo_status_pay"` Result int `json:"result"` SdgHeight int `json:"sdg_height"` SdgWidth int `json:"sdg_width"` Ticket string `json:"ticket"` UserAttribute string `json:"userAttribute"` Userid string `json:"userid"` } type LoginAutoResp struct { Result int `json:"result"` Message string `json:"message"` Autokey string `json:"autokey"` UserId string `json:"userid"` Ticket string `json:"ticket"` } type InitializeResp struct { BrandLogo string `json:"brand_logo"` BrandName string `json:"brand_name"` DaoyuClientid string `json:"daoyu_clientid"` DaoyuDownloadURL string `json:"daoyu_download_url"` DeviceFeature string `json:"device_feature"` DisplayThirdaccout int `json:"display_thirdaccout"` ForceShowAgreement int `json:"force_show_agreement"` GreportLogLevel string `json:"greport_log_level"` GuestEnable int `json:"guest_enable"` IsMatch int `json:"is_match"` LogLevel string `json:"log_level"` LoginButton []string `json:"login_button"` LoginIcon []any `json:"login_icon"` LoginLimitEnable int `json:"login_limit_enable"` NeedFloatWindowPermission int `json:"need_float_window_permission"` NewDeviceIDServer string `json:"new_device_id_server"` QqAppID string `json:"qq_appId"` QqKey string `json:"qq_key"` ShowGuestConfirm int `json:"show_guest_confirm"` VoicetipButton int `json:"voicetip_button"` VoicetipOne string `json:"voicetip_one"` VoicetipTwo string `json:"voicetip_two"` WegameAppid string `json:"wegame_appid"` WegameAppkey string `json:"wegame_appkey"` WegameClientid string `json:"wegame_clientid"` WegameCompanyID string `json:"wegame_companyId"` WegameLoginURL string `json:"wegame_loginUrl"` WeiboAppKey string `json:"weibo_appKey"` WeiboRedirectURL string `json:"weibo_redirectUrl"` WeixinAppID string `json:"weixin_appId"` WeixinKey string `json:"weixin_key"` } func Active(ctx *gin.Context) { // body, err := io.ReadAll(ctx.Request.Body) // CheckErr(err) // defer ctx.Request.Body.Close() // fmt.Println(string(body)) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, `{ "code": 0, "msg": "ok", "data": { "message": "ok", "result": 0 } }`) } func PublicKey(ctx *gin.Context) { publicKey := utils.ReadAllText(config.PublicKeyPath) publicKey = strings.ReplaceAll(publicKey, "\n", "") publicKey = strings.ReplaceAll(publicKey, "-----BEGIN PUBLIC KEY-----", "") publicKey = strings.ReplaceAll(publicKey, "-----END PUBLIC KEY-----", "") publicKey = strings.ReplaceAll(publicKey, "/", "\\/") // fmt.Println(publicKey) resp := fmt.Sprintf(`{ "code": 0, "msg": "", "data": { "result": 0, "message": "ok", "key": "%s", "method": "rsa" } }`, publicKey) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func Handshake(ctx *gin.Context) { body, err := io.ReadAll(ctx.Request.Body) CheckErr(err) defer ctx.Request.Body.Close() // fmt.Println(string(body)) body64, err := base64.StdEncoding.DecodeString(string(body)) CheckErr(err) decryptedBody := encrypt.RSADecrypt(body64) // fmt.Println(decryptedBody) // fmt.Println(string(decryptedBody)) params, err := url.ParseQuery(string(decryptedBody)) CheckErr(err) randKey := params.Get("randkey") deviceId := ctx.Request.Header.Get("X-DEVICEID") // fmt.Println(randKey) // fmt.Println(deviceId) err = db.DB.Set([]byte(deviceId), []byte(randKey)) CheckErr(err) token := strings.ToUpper(utils.RandomStr(33)) token = fmt.Sprintf(`{"message":"ok","result":0,"token":"%s"}`, token) encryptedToken, err := openssl.Des3ECBEncrypt([]byte(token), []byte(randKey)[0:24], openssl.PKCS7_PADDING) CheckErr(err) encryptedToken64 := base64.StdEncoding.EncodeToString(encryptedToken) // fmt.Println(encryptedToken64) resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedToken64) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func Initialize(ctx *gin.Context) { // body, err := io.ReadAll(ctx.Request.Body) // CheckErr(err) // defer ctx.Request.Body.Close() // fmt.Println(string(body)) // body64, err := base64.StdEncoding.DecodeString(string(body)) // CheckErr(err) // fmt.Println(string(body64)) deviceId := ctx.Request.Header.Get("X-DEVICEID") randKey, err := db.DB.Get([]byte(deviceId)) CheckErr(err) // decryptedBody, err := openssl.Des3ECBDecrypt(body64, randKey[0:24], openssl.PKCS7_PADDING) // CheckErr(err) // fmt.Println(string(decryptedBody)) initResp := InitializeResp{ BrandLogo: "http://gskd.sdo.com/ghome/ztc/logo/og/logo_xhdpi.png", BrandName: "盛趣游戏", ForceShowAgreement: 1, GreportLogLevel: "off", LogLevel: "off", LoginButton: []string{"official"}, LoginIcon: []any{}, NeedFloatWindowPermission: 1, NewDeviceIDServer: strings.ToUpper(openssl.Md5ToString(deviceId)), ShowGuestConfirm: 1, VoicetipButton: 1, } data, err := json.Marshal(initResp) CheckErr(err) encryptedData, err := openssl.Des3ECBEncrypt([]byte(data), randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) // fmt.Println(encryptedToken64) resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedData64) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func GetCode(ctx *gin.Context) { resp := `{ "code": 0, "msg": "ok", "data": { "codeArray": [ { "btntext": "好的", "code": "-10264022", "msg_from": 2, "text": "", "title": "短信验证码被阻止", "type": 1 }, { "btntext": " ", "code": "-10869623", "msg_from": 2, "text": "", "title": "网络连接失败,无法一键登录", "type": 2 }, { "btntext": " ", "code": "10298300", "msg_from": 2, "text": "", "title": " ", "type": 3 }, { "btntext": "", "code": "10298311", "msg_from": 2, "text": "", "title": "", "type": 3 }, { "btntext": " ", "code": "10298312", "msg_from": 2, "text": "", "title": " ", "type": 3 }, { "btntext": " ", "code": "10298313", "msg_from": 2, "text": "", "title": " ", "type": 1 }, { "btntext": " ", "code": "10298321", "msg_from": 2, "text": "", "title": " ", "type": 3 }, { "btntext": " ", "code": "10298322", "msg_from": 2, "text": "", "title": " ", "type": 3 } ], "codeVersion": "1.0.5" } }` ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func LoginAuto(ctx *gin.Context) { body, err := io.ReadAll(ctx.Request.Body) CheckErr(err) defer ctx.Request.Body.Close() // fmt.Println(string(body)) body64, err := base64.StdEncoding.DecodeString(string(body)) CheckErr(err) // fmt.Println(string(body64)) deviceId := ctx.Request.Header.Get("X-DEVICEID") randKey, err := db.DB.Get([]byte(deviceId)) CheckErr(err) decryptedBody, err := openssl.Des3ECBDecrypt(body64, randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) queryStr := string(decryptedBody) // fmt.Println(queryStr) params, err := url.ParseQuery(queryStr) CheckErr(err) autoKey := params.Get("autokey") // fmt.Println(autoKey) if autoKey == "" { ctx.String(http.StatusForbidden, ErrorMsg) return } var userId, ticket string _, err = UserEng.Table("users").Cols("userid,ticket").Where("autokey = ?", autoKey).Get(&userId, &ticket) CheckErr(err) var resp string if userId != "" { autoResp := LoginAutoResp{ Result: 0, Message: "ok", Autokey: autoKey, UserId: userId, Ticket: ticket, } data, err := json.Marshal(autoResp) // fmt.Println(string(data)) CheckErr(err) encryptedData, err := openssl.Des3ECBEncrypt(data, randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) // fmt.Println(encryptedData64) resp = fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedData64) } else { data := `{"message":"账号不存在或者登陆状态已过期!","result":31}` encryptedData, err := openssl.Des3ECBEncrypt([]byte(data), randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) // fmt.Println(encryptedData64) resp = fmt.Sprintf(`{ "code": 31, "msg": "账号不存在或者登陆状态已过期!", "data": "%s" }`, encryptedData64) } ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func LoginArea(ctx *gin.Context) { userId := ctx.PostForm("userid") if userId != "" { // fmt.Println(userId) resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": { "userid": "%s" } }`, userId) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } } func AccountLogin(ctx *gin.Context) { body, err := io.ReadAll(ctx.Request.Body) CheckErr(err) defer ctx.Request.Body.Close() // fmt.Println(string(body)) body64, err := base64.StdEncoding.DecodeString(string(body)) CheckErr(err) // fmt.Println(string(body64)) deviceId := ctx.Request.Header.Get("X-DEVICEID") randKey, err := db.DB.Get([]byte(deviceId)) CheckErr(err) decryptedBody, err := openssl.Des3ECBDecrypt(body64, randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) queryStr, err := url.QueryUnescape(string(decryptedBody)) CheckErr(err) params, err := url.ParseQuery(queryStr) CheckErr(err) phone, password := params.Get("phone"), params.Get("password") if phone == "" || password == "" { ctx.String(http.StatusForbidden, ErrorMsg) return } // sql := `CREATE TABLE "users" ( // "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, // "phone" TEXT, // "password" TEXT, // "autokey" TEXT, // "ticket" TEXT, // "userid" INTEGER, // "last_login_time" INTEGER // ); // CREATE TABLE "user_key" ( // "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, // "userid" INTEGER, // "key" INTEGER // );` var pass, autoKey, ticket, userId string _, err = UserEng.Table("users").Cols("password,autokey,ticket,userid").Where("phone = ?", phone). Get(&pass, &autoKey, &ticket, &userId) CheckErr(err) loginResp := LoginResp{} loginCode := 0 loginMsg := "ok" loginTime := time.Now().Unix() if pass == "" { // 未注册 - 自动注册 session := UserEng.NewSession() defer session.Close() // 开始会话 if err = session.Begin(); err != nil { session.Rollback() panic(err) } pass = openssl.Md5ToString(password) autoKey = "AUTO" + strings.ToUpper(utils.RandomStr(32)) userId = strconv.Itoa(int(loginTime)) ticket = "9999999" + userId + userId userStmt, err := session.DB().Prepare("INSERT INTO users(phone,password,autokey,ticket,userid,last_login_time) VALUES (?,?,?,?,?,?)") if err != nil { session.Rollback() panic(err) } defer userStmt.Close() _, err = userStmt.Exec(phone, pass, autoKey, ticket, userId, loginTime) if err != nil { session.Rollback() panic(err) } // id, _ := res.LastInsertId() // fmt.Println("LastInsertId:", id) keyStmt, err := session.DB().Prepare("INSERT INTO user_key(userid,key) VALUES(?,?)") if err != nil { session.Rollback() panic(err) } // 方便起见初始化 userid 和 key 一样 // 注意:user_key 表中的 key 是上文生成的用于登录的 userid,而 userid 则是用于 Authorize Token 生成用的 _, err = keyStmt.Exec(userId, userId) if err != nil { session.Rollback() panic(err) } // 结束会话 if err = session.Commit(); err != nil { session.Rollback() panic(err) } tools.InitUserData(int(loginTime)) // Login Response loginResp.Autokey = autoKey loginResp.HasRealInfo = 1 loginResp.Message = "ok" loginResp.RealInfoForce = 1 loginResp.Ticket = ticket loginResp.UserAttribute = "0" loginResp.Userid = userId } else { // 已注册 if openssl.Md5ToString(password) == pass { // 密码正确 // Login Response loginResp.Autokey = autoKey // 注意:更换设备(deviceId 发生变化)应重新生成 autokey loginResp.HasRealInfo = 1 loginResp.Message = "ok" loginResp.RealInfoForce = 1 loginResp.Ticket = "9999999" + userId + strconv.Itoa(int(loginTime)) // 实际登录用的密码(每次登录都会重新生成新的) loginResp.UserAttribute = "0" loginResp.Userid = userId // 实际登录用的账号 // 更新信息 userStmt, err := UserEng.DB().Prepare("UPDATE users SET autokey=?,ticket=?,last_login_time=? WHERE userid=?") CheckErr(err) defer userStmt.Close() _, err = userStmt.Exec(autoKey, ticket, loginTime, userId) CheckErr(err) // aff, _ := res.RowsAffected() // fmt.Println("RowsAffected:", aff) } else { // 密码错误 loginCode = 31 loginMsg = "账号不存在或者密码有误!" } } data, err := json.Marshal(loginResp) CheckErr(err) // fmt.Println(string(data)) encryptedData, err := openssl.Des3ECBEncrypt(data, randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) encryptedData64 := base64.StdEncoding.EncodeToString(encryptedData) // fmt.Println(encryptedToken64) resp := fmt.Sprintf(`{ "code": %d, "msg": "%s", "data": "%s" }`, loginCode, loginMsg, encryptedData64) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func ReportRole(ctx *gin.Context) { // body, err := io.ReadAll(ctx.Request.Body) // CheckErr(err) // defer ctx.Request.Body.Close() // fmt.Println(string(body)) // body64, err := base64.StdEncoding.DecodeString(string(body)) // CheckErr(err) // fmt.Println(string(body64)) deviceId := ctx.Request.Header.Get("X-DEVICEID") randKey, err := db.DB.Get([]byte(deviceId)) CheckErr(err) // decryptedBody, err := openssl.Des3ECBDecrypt(body64, randKey[0:24], openssl.PKCS7_PADDING) // CheckErr(err) // fmt.Println(string(decryptedBody)) // decrypted, err := url.QueryUnescape(string(decryptedBody)) // CheckErr(err) // fmt.Println(decrypted) // Unable to decrypt server data token := `{"message":"Hello, world!"}` encryptedToken, err := openssl.Des3ECBEncrypt([]byte(token), randKey[0:24], openssl.PKCS7_PADDING) CheckErr(err) encryptedToken64 := base64.StdEncoding.EncodeToString(encryptedToken) // fmt.Println(encryptedToken64) resp := fmt.Sprintf(`{ "code": 0, "msg": "ok", "data": "%s" }`, encryptedToken64) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func GetProductList(ctx *gin.Context) { resp := `{ "code": 0, "msg": "ok", "data": { "message": [ ], "result": 0 } }` ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func GuestStatus(ctx *gin.Context) { resp := `{"code":0,"msg":"ok","data":{"disablead":1,"loginswitch":1,"message":"ok","result":0}}` ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func ReportLog(ctx *gin.Context) { // body, err := io.ReadAll(ctx.Request.Body) // CheckErr(err) // defer ctx.Request.Body.Close() // fmt.Println(string(body)) ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, "") } func ReportApp(ctx *gin.Context) { // body, err := io.ReadAll(ctx.Request.Body) // CheckErr(err) // defer ctx.Request.Body.Close() // fmt.Println(string(body)) resp := `{ "code": 0, "msg": "", "data": { "needReport": 0 } }` ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) } func Agreement(ctx *gin.Context) { resp := `{ "return_code": 0, "error_type": 0, "return_message": "", "data": { } }` ctx.Header("Content-Type", "text/html;charset=utf-8") ctx.String(http.StatusOK, resp) }